diff --git a/README.md b/README.md
index c467fba49..549733a59 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
# Chameleon-Vision
-[](https://circleci.com/gh/Chameleon-Vision/workflows/chameleon-vision/tree/dev)
-[](https://circleci.com/gh/Chameleon-Vision/workflows/chameleon-vision/tree/master)
+[](https://circleci.com/gh/Chameleon-Vision/chameleon-vision/tree/dev)
+[](https://circleci.com/gh/Chameleon-Vision/chameleon-vision/tree/master)
Chameleon Vision is free open-source software for FRC teams to use for vision proccesing on their robots.
diff --git a/chameleon-client/src/assets/FRCtargets.json b/chameleon-client/src/assets/FRCtargets.json
new file mode 100644
index 000000000..12a8babe4
--- /dev/null
+++ b/chameleon-client/src/assets/FRCtargets.json
@@ -0,0 +1,14 @@
+{
+ "2020 Hex Goal": [
+ [-19.625, 0],
+ [-9.819867, -17],
+ [9.819867, -17],
+ [19.625,0]
+ ],
+ "2019 Dual Target": [
+ [-7.75, 3],
+ [-7.75, -3],
+ [7.75, -3],
+ [7.75, 3]
+ ]
+}
\ No newline at end of file
diff --git a/chameleon-client/src/views/CameraViewes/3D.vue b/chameleon-client/src/views/CameraViewes/3D.vue
index 6d551b7ed..5a76c92c1 100644
--- a/chameleon-client/src/views/CameraViewes/3D.vue
+++ b/chameleon-client/src/views/CameraViewes/3D.vue
@@ -4,35 +4,44 @@
-
-
-
-
-
-
+
+
+
+ mdi-upload
+ upload model
+
+
+
+
+
+
+
+
+
+ Upload Premade
+
-
diff --git a/chameleon-client/src/views/CameraViewes/InputTab.vue b/chameleon-client/src/views/CameraViewes/InputTab.vue
index f844eb445..181b907bc 100644
--- a/chameleon-client/src/views/CameraViewes/InputTab.vue
+++ b/chameleon-client/src/views/CameraViewes/InputTab.vue
@@ -2,9 +2,12 @@
+
-
+
@@ -46,9 +49,9 @@
streamResolutionList: {
get() {
let cam_res = this.$store.state.resolutionList[this.value.videoModeIndex];
- let tmp_list = [];
+ let tmp_list = [];
tmp_list.push(`${Math.floor(cam_res['width'])} X ${Math.floor(cam_res['height'])}`);
- for (let x = 2; x <= 6; x+=2) {
+ for (let x = 2; x <= 6; x += 2) {
tmp_list.push(`${Math.floor(cam_res['width'] / x)} X ${Math.floor(cam_res['height'] / x)}`);
}
return tmp_list;
diff --git a/chameleon-server/chameleon-vision.iml b/chameleon-server/chameleon-vision.iml
index 93abb3b87..11b1a05d0 100644
--- a/chameleon-server/chameleon-vision.iml
+++ b/chameleon-server/chameleon-vision.iml
@@ -49,20 +49,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chameleon-server/pom.xml b/chameleon-server/pom.xml
index 1eddbcfc4..c5a1f3c64 100644
--- a/chameleon-server/pom.xml
+++ b/chameleon-server/pom.xml
@@ -5,7 +5,8 @@
4.0.0
org.chameleon-vision.main
chameleon-vision
- 2.1.1-RELEASE
+
+ 2.2-RELEASE
@@ -44,8 +45,11 @@
UTF-8
- 2020.1.2
+ 2020.2.2
3.4.7-2
+
+
+ 12
diff --git a/chameleon-server/src/main/java/com/chameleonvision/Main.java b/chameleon-server/src/main/java/com/chameleonvision/Main.java
index 53c8953fa..7c97b9fe9 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/Main.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/Main.java
@@ -2,20 +2,17 @@ package com.chameleonvision;
import com.chameleonvision.config.ConfigManager;
import com.chameleonvision.network.NetworkManager;
+import com.chameleonvision.networktables.NetworkTablesManager;
import com.chameleonvision.scripting.ScriptEventType;
import com.chameleonvision.scripting.ScriptManager;
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;
import edu.wpi.cscore.CameraServerCvJNI;
import edu.wpi.cscore.CameraServerJNI;
-import edu.wpi.first.networktables.LogMessage;
-import edu.wpi.first.networktables.NetworkTableInstance;
import java.io.IOException;
-import java.util.function.Consumer;
import static com.chameleonvision.util.Platform.CurrentPlatform;
@@ -26,6 +23,7 @@ public class Main {
private static final String NETWORK_MANAGE_KEY = "--unmanage-network"; // no args for this setting
private static final String IGNORE_ROOT_KEY = "--ignore-root"; // no args for this setting
private static final String TEST_MODE_KEY = "--cv-development";
+ private static final String UI_PORT_KEY = "--ui-port";
private static final int DEFAULT_PORT = 5800;
@@ -34,21 +32,7 @@ public class Main {
private static boolean ignoreRoot = false;
private static String ntClientModeServer = null;
public static boolean testMode = false;
-
- private static class NTLogger implements Consumer {
-
- private boolean hasReportedConnectionFailure = false;
-
- @Override
- public void accept(LogMessage logMessage) {
- if (!hasReportedConnectionFailure && logMessage.message.contains("timed out")) {
- System.err.println("NT Connection has failed!");
- hasReportedConnectionFailure = true;
- } else if (logMessage.message.contains("connected")) {
- ScriptManager.queueEvent(ScriptEventType.kNTConnected);
- }
- }
- }
+ public static int uiPort = DEFAULT_PORT;
private static void handleArgs(String[] args) {
for (int i = 0; i < args.length; i++) {
@@ -65,6 +49,13 @@ public class Main {
}
i++; // increment to skip an 'arg' next go-around of for loop, as that would be this value
break;
+ case UI_PORT_KEY:
+ var potentialPort = args[i + 1];
+ if (potentialPort != null && !potentialPort.isBlank() && !potentialPort.startsWith("-") & !potentialPort.startsWith("--")) {
+ value = potentialPort;
+ }
+ i++;
+ break;
case NT_SERVERMODE_KEY:
case NETWORK_MANAGE_KEY:
case IGNORE_ROOT_KEY:
@@ -101,6 +92,15 @@ public class Main {
case TEST_MODE_KEY:
testMode = true;
break;
+ case UI_PORT_KEY:
+ if (value != null) {
+ try {
+ uiPort = Integer.parseInt(value);
+ } catch (NumberFormatException e){
+ System.err.println("ui Port was not a valid number using port 5800");
+ }
+ }
+ break;
}
}
}
@@ -124,7 +124,7 @@ public class Main {
System.out.println("Ignoring root, network will not be managed!");
} else {
System.err.println("This program must be run as root!");
- return;
+ return;
}
}
@@ -133,7 +133,7 @@ public class Main {
CameraServerJNI.forceLoad();
CameraServerCvJNI.forceLoad();
} catch (UnsatisfiedLinkError | IOException e) {
- if(CurrentPlatform.isWindows()) {
+ if (CurrentPlatform.isWindows()) {
System.err.println("Try to download the VC++ Redistributable, https://aka.ms/vs/16/release/vc_redist.x64.exe");
}
throw new RuntimeException("Failed to load JNI Libraries!");
@@ -147,20 +147,12 @@ public class Main {
System.out.println("Scripts not yet supported on Windows. ScriptEvents will be ignored.");
}
-
NetworkManager.initialize(manageNetwork);
if (ntServerMode) {
- System.out.println("Starting NT Server");
- NetworkTableInstance.getDefault().startServer();
+ NetworkTablesManager.setServerMode();
} else {
- NetworkTableInstance.getDefault().addLogger(new NTLogger(), 0, 255); // to hide error messages
- if (ntClientModeServer != null) {
- NetworkTableInstance.getDefault().startClient(ntClientModeServer);
- } else {
- NetworkTableInstance.getDefault().startClientTeam(ConfigManager.settings.teamNumber);
- }
-// NetworkTableInstance.getDefault().startClient("localhost");
+ NetworkTablesManager.setClientMode(ntClientModeServer);
}
ScriptManager.queueEvent(ScriptEventType.kProgramInit);
@@ -179,7 +171,7 @@ public class Main {
VisionManager.startProcesses();
- System.out.printf("Starting Web server at port %d\n", DEFAULT_PORT);
- Server.main(DEFAULT_PORT);
+ System.out.printf("Starting Web server at port %d\n", uiPort);
+ Server.main(uiPort);
}
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
index 66d9df0da..b4176e3fa 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java
@@ -51,7 +51,7 @@ public class CameraConfig {
private CameraJsonConfig loadConfig() {
CameraJsonConfig config = preliminaryConfig;
try {
- config = JacksonHelper.deserializer(configPath, CameraJsonConfig.class);
+ config = JacksonHelper.deserialize(configPath, CameraJsonConfig.class);
} catch (IOException e) {
System.err.printf("Failed to load camera config: %s - using default.\n", configPath.toString());
}
@@ -61,7 +61,7 @@ public class CameraConfig {
private CVPipelineSettings loadDriverMode() {
CVPipelineSettings driverMode = new CVPipelineSettings();
try {
- driverMode = JacksonHelper.deserializer(driverModePath, CVPipelineSettings.class);
+ driverMode = JacksonHelper.deserialize(driverModePath, CVPipelineSettings.class);
} catch (IOException e) {
System.err.println("Failed to load camera drivermode: " + driverModePath.toString());
}
@@ -75,7 +75,7 @@ public class CameraConfig {
private List loadCalibration() {
List calibrations = new ArrayList<>();
try {
- calibrations = List.of(Objects.requireNonNull(JacksonHelper.deserializer(calibrationPath, CameraCalibrationConfig[].class)));
+ calibrations = List.of(Objects.requireNonNull(JacksonHelper.deserialize(calibrationPath, CameraCalibrationConfig[].class)));
} catch (Exception e) {
System.err.println("Failed to load camera calibration: " + driverModePath.toString());
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
index 18da8285e..fd0f5dc5d 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java
@@ -1,6 +1,5 @@
package com.chameleonvision.config;
-import com.chameleonvision.Main;
import com.chameleonvision.util.*;
import com.chameleonvision.vision.pipeline.CVPipelineSettings;
@@ -54,7 +53,7 @@ public class ConfigManager {
}
} else {
try {
- settings = JacksonHelper.deserializer(settingsFilePath, GeneralSettings.class);
+ settings = JacksonHelper.deserialize(settingsFilePath, GeneralSettings.class);
} catch (IOException e) {
System.err.println("Failed to load settings.json, using defaults.");
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
index e4d1c4757..256c6b784 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java
@@ -1,5 +1,7 @@
package com.chameleonvision.config;
+import com.chameleonvision.config.serializers.StandardCVPipelineSettingsDeserializer;
+import com.chameleonvision.config.serializers.StandardCVPipelineSettingsSerializer;
import com.chameleonvision.util.FileHelper;
import com.chameleonvision.util.JacksonHelper;
import com.chameleonvision.vision.pipeline.*;
@@ -71,11 +73,20 @@ public class PipelineConfig {
var path = getPipelinePath(settings);
- try {
- JacksonHelper.serializer(path, settings);
- FileHelper.setFilePerms(path);
- } catch (IOException e) {
- e.printStackTrace();
+ if (settings instanceof StandardCVPipelineSettings) {
+ try {
+ JacksonHelper.serialize(path, (StandardCVPipelineSettings)settings, StandardCVPipelineSettings.class, new StandardCVPipelineSettingsSerializer());
+ FileHelper.setFilePerms(path);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ JacksonHelper.serializer(path, settings);
+ FileHelper.setFilePerms(path);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
@@ -119,7 +130,7 @@ public class PipelineConfig {
} else {
for(File pipelineFile : pipelineFiles) {
try {
- var pipe = JacksonHelper.deserializer(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class);
+ var pipe = JacksonHelper.deserialize(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class, new StandardCVPipelineSettingsDeserializer());
deserializedList.add(pipe);
} catch (IOException e) {
System.err.println("couldn't load cvpipeline2d");
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java
new file mode 100644
index 000000000..69c5b86b3
--- /dev/null
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java
@@ -0,0 +1,110 @@
+package com.chameleonvision.config.serializers;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.jetbrains.annotations.NotNull;
+import org.opencv.core.MatOfPoint3f;
+import org.opencv.core.Point3;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class BaseDeserializer extends StdDeserializer {
+ protected BaseDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ JsonNode baseNode;
+
+ private static final CollectionType numberListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Number.class);
+ private CollectionType pointListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Object.class);
+ private static final ObjectMapper mapper = new ObjectMapper();
+ private static boolean nodeGood(JsonNode node) {
+ return node != null && !node.toString().equals("");
+ }
+
+ List getNumberList(String name, List defaultValue) throws JsonProcessingException {
+ JsonNode node = baseNode.get(name);
+
+ if (nodeGood(node)) {
+ return mapper.readValue(node.toString(), numberListColType);
+ }
+ return defaultValue;
+ }
+
+ boolean getBoolean(String name, boolean defaultValue) {
+ JsonNode node = baseNode.get(name);
+
+ if (nodeGood(node)) {
+ return node.booleanValue();
+ }
+
+ return defaultValue;
+ }
+
+ int getInt(String name, int defaultValue) {
+ return (int) getDouble(name, defaultValue);
+ }
+
+ double getDouble(String name, double defaultValue) {
+ JsonNode node = baseNode.get(name);
+
+ if (nodeGood(node)) {
+ return node.numberValue().doubleValue();
+ }
+
+ return defaultValue;
+ }
+
+ String getString(String name, String defaultValue) {
+ JsonNode node = baseNode.get(name);
+
+ if (nodeGood(node)) {
+ return node.asText();
+ }
+
+ return defaultValue;
+ }
+
+ > E getEnum(String name, Class enumClass, E defaultValue) throws IOException {
+ JsonNode node = baseNode.get(name);
+
+ if (nodeGood(node)) {
+ E[] possibleVals = enumClass.getEnumConstants();
+ String jsonVal = baseNode.get(name).asText();
+
+ for (E val : possibleVals) {
+ if (val.name().equals(jsonVal)) {
+ return val;
+ }
+ }
+ }
+
+ return defaultValue;
+ }
+ MatOfPoint3f getMatOfPoint3f(String name, MatOfPoint3f defaultValue) throws JsonProcessingException {
+ JsonNode node = baseNode.get(name);
+ if (nodeGood(node)){
+ List> numberList = mapper.readValue(node.toString(), pointListColType);
+ List point3List = new ArrayList<>();
+ for (List tmp : numberList){
+ Point3 p = new Point3();
+ p.x = tmp.get(0).doubleValue();
+ p.y = tmp.get(1).doubleValue();
+ p.z = tmp.get(2).doubleValue();
+ point3List.add(p);
+ }
+ MatOfPoint3f mat = new MatOfPoint3f();
+ mat.fromList(point3List);
+ return mat;
+ }
+
+ return defaultValue;
+ }
+}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java
new file mode 100644
index 000000000..fdfbb9db8
--- /dev/null
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java
@@ -0,0 +1,43 @@
+package com.chameleonvision.config.serializers;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import org.opencv.core.MatOfPoint3f;
+import org.opencv.core.Point;
+import org.opencv.core.Point3;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class BaseSerializer extends StdSerializer {
+ protected BaseSerializer(Class t) {
+ super(t);
+ }
+
+ JsonGenerator generator;
+
+ void writeNumberListAsNumberArray(String name, List list) throws IOException {
+ generator.writeArrayFieldStart(name);
+ for (Number i : list) {
+ generator.writeObject(i);
+ }
+ generator.writeEndArray();
+ }
+
+ > void writeEnum(String name, E num) throws IOException {
+ generator.writeFieldName(name);
+ generator.writeString(num.name());
+ }
+
+ void writeMatOfPoint3f(String name, MatOfPoint3f mat) throws IOException {
+ List point3List = mat.toList();
+ generator.writeArrayFieldStart(name);
+
+ for (Point3 point3 : point3List) {
+ double[] tmp = {point3.x, point3.y, point3.z};
+ generator.writeObject(tmp);
+ }
+ generator.writeEndArray();
+ }
+}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java
new file mode 100644
index 000000000..ec909e477
--- /dev/null
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java
@@ -0,0 +1,80 @@
+package com.chameleonvision.config.serializers;
+
+import com.chameleonvision.vision.enums.*;
+import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+
+import java.io.IOException;
+
+public class StandardCVPipelineSettingsDeserializer extends BaseDeserializer {
+ public StandardCVPipelineSettingsDeserializer() {
+ this(null);
+ }
+
+ private StandardCVPipelineSettingsDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public StandardCVPipelineSettings deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ // set BaseDeserializer parser reference.
+ baseNode = jsonParser.getCodec().readTree(jsonParser);
+
+ StandardCVPipelineSettings pipeline = new StandardCVPipelineSettings();
+
+ pipeline.index = getInt("index", pipeline.index);
+
+ pipeline.flipMode = getEnum("flipMode", ImageFlipMode.class, pipeline.flipMode);
+ pipeline.rotationMode = getEnum("rotationMode", ImageRotationMode.class, pipeline.rotationMode);
+
+ pipeline.nickname = getString("nickname", pipeline.nickname);
+
+ pipeline.exposure = getDouble("exposure", pipeline.exposure);
+ pipeline.brightness = getDouble("brightness", pipeline.brightness);
+ pipeline.gain = getDouble("gain", pipeline.gain);
+
+ pipeline.videoModeIndex = getInt("videoModeIndex", pipeline.videoModeIndex);
+
+ pipeline.streamDivisor = getEnum("streamDivisor", StreamDivisor.class, pipeline.streamDivisor);
+
+ pipeline.hue = getNumberList("hue", pipeline.hue);
+ pipeline.saturation = getNumberList("saturation", pipeline.saturation);
+ pipeline.value = getNumberList("value", pipeline.value);
+
+ pipeline.erode = getBoolean("erode", pipeline.erode);
+ pipeline.dilate = getBoolean("dilate", pipeline.dilate);
+
+ pipeline.area = getNumberList("area", pipeline.area);
+ pipeline.ratio = getNumberList("ratio", pipeline.ratio);
+ pipeline.extent = getNumberList("extent", pipeline.extent);
+
+ pipeline.speckle = getInt("speckle", (Integer) pipeline.speckle);
+
+ pipeline.isBinary = getBoolean("isBinary", pipeline.isBinary);
+
+ pipeline.sortMode = getEnum("sortMode", SortMode.class, pipeline.sortMode);
+ pipeline.targetRegion = getEnum("targetRegion", TargetRegion.class, pipeline.targetRegion);
+ pipeline.targetOrientation = getEnum("targetOrientation", TargetOrientation.class, pipeline.targetOrientation);
+
+ pipeline.multiple = getBoolean("multiple", pipeline.multiple);
+
+ pipeline.targetGroup = getEnum("targetGroup", TargetGroup.class, pipeline.targetGroup);
+ pipeline.targetIntersection = getEnum("targetIntersection", TargetIntersection.class, pipeline.targetIntersection);
+
+ pipeline.point = getNumberList("point", pipeline.point);
+
+ pipeline.calibrationMode = getEnum("calibrationMode", CalibrationMode.class, pipeline.calibrationMode);
+
+ pipeline.dualTargetCalibrationM = getDouble("dualTargetCalibrationM", pipeline.dualTargetCalibrationM);
+ pipeline.dualTargetCalibrationB = getDouble("dualTargetCalibrationB", pipeline.dualTargetCalibrationB);
+
+ pipeline.is3D = getBoolean("is3D", pipeline.is3D);
+ pipeline.targetCornerMat = getMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat);
+
+ return pipeline;
+ }
+}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java
new file mode 100644
index 000000000..c787d3145
--- /dev/null
+++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java
@@ -0,0 +1,82 @@
+package com.chameleonvision.config.serializers;
+
+import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+
+public class StandardCVPipelineSettingsSerializer extends BaseSerializer {
+ public StandardCVPipelineSettingsSerializer() {
+ this(null);
+ }
+
+ private StandardCVPipelineSettingsSerializer(Class t) {
+ super(t);
+ }
+
+ @Override
+ public void serialize(StandardCVPipelineSettings pipeline, JsonGenerator gen, SerializerProvider provider) throws IOException {
+ // set BaseSerializer generator reference.
+ generator = gen;
+
+ gen.writeStartObject();
+
+ gen.writeNumberField("index", pipeline.index);
+
+ writeEnum("flipMode", pipeline.flipMode);
+ writeEnum("rotationMode", pipeline.rotationMode);
+
+ gen.writeStringField("nickname", pipeline.nickname);
+
+ gen.writeNumberField("exposure", pipeline.exposure);
+ gen.writeNumberField("brightness", pipeline.brightness);
+ gen.writeNumberField("gain", pipeline.gain);
+
+ gen.writeNumberField("videoModeIndex", pipeline.videoModeIndex);
+
+ writeEnum("streamDivisor", pipeline.streamDivisor);
+
+ writeNumberListAsNumberArray("hue", pipeline.hue);
+ writeNumberListAsNumberArray("saturation", pipeline.saturation);
+ writeNumberListAsNumberArray("value", pipeline.value);
+
+ gen.writeBooleanField("erode", pipeline.erode);
+ gen.writeBooleanField("dilate", pipeline.dilate);
+
+ writeNumberListAsNumberArray("area", pipeline.area);
+ writeNumberListAsNumberArray("ratio", pipeline.ratio);
+ writeNumberListAsNumberArray("extent", pipeline.extent);
+
+ // speckle rejection
+ gen.writeNumberField("speckle", (Integer) pipeline.speckle);
+
+ // stream output (camera feed, or thresholded feed)
+ gen.writeBooleanField("isBinary", pipeline.isBinary);
+
+ writeEnum("sortMode", pipeline.sortMode);
+ writeEnum("targetRegion", pipeline.targetRegion);
+ writeEnum("targetOrientation", pipeline.targetOrientation);
+
+ // show multiple targets when drawing
+ gen.writeBooleanField("multiple", pipeline.multiple);
+
+ writeEnum("targetGroup", pipeline.targetGroup);
+ writeEnum("targetIntersection", pipeline.targetIntersection);
+
+ // single calibration point
+ writeNumberListAsNumberArray("point", pipeline.point);
+
+ // target X/Y calibration
+ writeEnum("calibrationMode", pipeline.calibrationMode);
+
+ // TODO: better names? or use an array?
+ gen.writeNumberField("dualTargetCalibrationM", pipeline.dualTargetCalibrationM);
+ gen.writeNumberField("dualTargetCalibrationB", pipeline.dualTargetCalibrationB);
+
+
+ gen.writeBooleanField("is3D", pipeline.is3D);
+ writeMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat);
+ gen.writeEndObject();
+ }
+}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java b/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java
new file mode 100644
index 000000000..de020466c
--- /dev/null
+++ b/chameleon-server/src/main/java/com/chameleonvision/networktables/NetworkTablesManager.java
@@ -0,0 +1,69 @@
+package com.chameleonvision.networktables;
+
+import com.chameleonvision.config.ConfigManager;
+import com.chameleonvision.scripting.ScriptEventType;
+import com.chameleonvision.scripting.ScriptManager;
+import edu.wpi.first.networktables.LogMessage;
+import edu.wpi.first.networktables.NetworkTable;
+import edu.wpi.first.networktables.NetworkTableInstance;
+
+import java.util.function.Consumer;
+
+public class NetworkTablesManager {
+
+ private NetworkTablesManager() {}
+
+ private static final NetworkTableInstance NTInst = NetworkTableInstance.getDefault();
+
+ public static final String kRootTableName = "/chameleon-vision";
+ public static final NetworkTable kRootTable = NetworkTableInstance.getDefault().getTable(kRootTableName);
+
+ public static boolean isServer = false;
+
+ private static int getTeamNumber() {
+ return ConfigManager.settings.teamNumber;
+ }
+
+ private static class NTLogger implements Consumer {
+
+ private boolean hasReportedConnectionFailure = false;
+
+ @Override
+ public void accept(LogMessage logMessage) {
+ if (!hasReportedConnectionFailure && logMessage.message.contains("timed out")) {
+ System.err.println("NT Connection has failed! Will retry in background.");
+ hasReportedConnectionFailure = true;
+ } else if (logMessage.message.contains("connected")) {
+ System.out.println("NT Connected!");
+ hasReportedConnectionFailure = false;
+ ScriptManager.queueEvent(ScriptEventType.kNTConnected);
+ }
+ }
+ }
+
+ static {
+ NetworkTableInstance.getDefault().addLogger(new NTLogger(), 0, 255); // to hide error messages
+ }
+
+ public static void setClientMode(String host) {
+ isServer = false;
+ System.out.println("Starting NT Client");
+ NTInst.stopServer();
+ if (host != null) {
+ NTInst.startClient(host);
+ } else {
+ NTInst.startClientTeam(getTeamNumber());
+ }
+ }
+
+ public static void setTeamClientMode() {
+ setClientMode(null);
+ }
+
+ public static void setServerMode() {
+ isServer = true;
+ System.out.println("Starting NT Server");
+ NTInst.stopClient();
+ NTInst.startServer();
+ }
+}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
index 9385a13c3..f85e3fb61 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java
@@ -1,24 +1,18 @@
package com.chameleonvision.scripting;
import com.chameleonvision.Debug;
-import com.chameleonvision.Main;
import com.chameleonvision.config.ConfigManager;
import com.chameleonvision.util.JacksonHelper;
import com.chameleonvision.util.LoopingRunnable;
import com.chameleonvision.util.Platform;
-import com.chameleonvision.util.ProgramDirectoryUtilities;
-import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.LinkedBlockingQueue;
public class ScriptManager {
@@ -94,7 +88,7 @@ public class ScriptManager {
static List loadConfig() {
try {
- var raw = JacksonHelper.deserializer(scriptConfigPath, ScriptConfig[].class);
+ var raw = JacksonHelper.deserialize(scriptConfigPath, ScriptConfig[].class);
if (raw != null) {
return List.of(raw);
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java b/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java
index 13ff9e3a2..04d97fc2c 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java
@@ -1,5 +1,7 @@
package com.chameleonvision.util;
+import com.chameleonvision.Debug;
+
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -20,7 +22,7 @@ public class FileHelper {
File thisFile = path.toFile();
Set perms = Files.readAttributes(path, PosixFileAttributes.class).permissions();
if (!perms.equals(allReadWriteExecutePerms)) {
- System.out.printf("setting perms on %s\n", path.toString());
+ Debug.printInfo("Setting perms on" + path.toString());
Files.setPosixFilePermissions(path, perms);
if (thisFile.isDirectory()) {
for (File subfile : thisFile.listFiles()) {
diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java b/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
index d63db5428..f63f5d1e7 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/util/Helpers.java
@@ -12,7 +12,7 @@ public class Helpers {
}
public static Scalar colorToScalar(Color color) {
- return new Scalar(color.getRed(), color.getGreen(), color.getBlue());
+ return new Scalar(color.getBlue(), color.getGreen(), color.getRed());
}
public static HashMap VideoModeToHashMap(VideoMode videoMode) {
diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
index 8979b4621..d6da705a7 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java
@@ -1,9 +1,14 @@
package com.chameleonvision.util;
+import com.chameleonvision.config.serializers.StandardCVPipelineSettingsDeserializer;
+import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.File;
import java.io.IOException;
@@ -18,7 +23,7 @@ public class JacksonHelper {
objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object);
}
- public static T deserializer(Path path, Class ref) throws IOException {
+ public static T deserialize(Path path, Class ref) throws IOException {
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build();
ObjectMapper objectMapper = JsonMapper.builder().activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT).build();
File jsonFile = new File(path.toString());
@@ -27,4 +32,26 @@ public class JacksonHelper {
}
return null;
}
+
+ public static T deserialize(Path path, Class ref, StdDeserializer deserializer) throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(ref, deserializer);
+ objectMapper.registerModule(module);
+
+ File jsonFile = new File(path.toString());
+ if (jsonFile.exists() && jsonFile.length() > 0) {
+ return objectMapper.readValue(jsonFile, ref);
+ }
+ return null;
+ }
+
+ public static void serialize(Path path, T object, Class ref, StdSerializer serializer) throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ SimpleModule module = new SimpleModule();
+ module.addSerializer(ref, serializer);
+ objectMapper.registerModule(module);
+
+ objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object);
+ }
}
diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
index 17831d1ad..fdf464a13 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionManager.java
@@ -15,9 +15,9 @@ import org.opencv.videoio.VideoCapture;
import java.util.*;
import java.util.stream.Collectors;
+@SuppressWarnings("rawtypes")
public class VisionManager {
- private VisionManager() {
- }
+ private VisionManager() {}
private static final LinkedHashMap usbCameraInfosByCameraName = new LinkedHashMap<>();
private static final LinkedList loadedCameraConfigs = new LinkedList<>();
diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
index 573506c0c..a1776398c 100644
--- a/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
+++ b/chameleon-server/src/main/java/com/chameleonvision/vision/VisionProcess.java
@@ -4,6 +4,7 @@ import com.chameleonvision.Debug;
import com.chameleonvision.config.CameraCalibrationConfig;
import com.chameleonvision.config.CameraConfig;
import com.chameleonvision.config.ConfigManager;
+import com.chameleonvision.networktables.NetworkTablesManager;
import com.chameleonvision.scripting.ScriptEventType;
import com.chameleonvision.scripting.ScriptManager;
import com.chameleonvision.config.FullCameraConfiguration;
@@ -32,10 +33,10 @@ import java.util.concurrent.LinkedBlockingDeque;
import java.util.stream.Collectors;
+@SuppressWarnings("rawtypes")
public class VisionProcess {
- private final USBCameraCapture cameraCapture;
-// private final CameraStreamerRunnable streamRunnable;
+ public final USBCameraCapture cameraCapture;
private final VisionProcessRunnable visionRunnable;
private final CameraConfig fileConfig;
public final CameraStreamer cameraStreamer;
@@ -43,8 +44,6 @@ public class VisionProcess {
private volatile CVPipelineResult lastPipelineResult;
- private BlockingQueue streamFrameQueue = new LinkedBlockingDeque<>(1);
-
// network table stuff
private final NetworkTable defaultTable;
private NetworkTableInstance tableInstance;
@@ -59,6 +58,12 @@ public class VisionProcess {
private NetworkTableEntry ntLatencyEntry;
private NetworkTableEntry ntValidEntry;
private NetworkTableEntry ntPoseEntry;
+ private NetworkTableEntry ntFittedHeightEntry;
+ private NetworkTableEntry ntFittedWidthEntry;
+ private NetworkTableEntry ntBoundingHeightEntry;
+ private NetworkTableEntry ntBoundingWidthEntry;
+ private NetworkTableEntry ntTargetRotation;
+
private ObjectMapper objectMapper = new ObjectMapper();
private long lastUIUpdateMs = 0;
@@ -72,7 +77,6 @@ public class VisionProcess {
// Thread to put frames on the dashboard
this.cameraStreamer = new CameraStreamer(cameraCapture, config.cameraConfig.name, pipelineManager.getCurrentPipeline().settings.streamDivisor);
-// this.streamRunnable = new CameraStreamerRunnable(30, cameraStreamer);
// Thread to process vision data
this.visionRunnable = new VisionProcessRunnable();
@@ -110,21 +114,26 @@ public class VisionProcess {
public void setCameraNickname(String newName) {
getCamera().getProperties().setNickname(newName);
- var newTable = NetworkTableInstance.getDefault().getTable("/chameleon-vision/" + newName);
- resetNT(newTable);
+ NetworkTable camTable = NetworkTablesManager.kRootTable.getSubTable(newName);
+ resetNT(camTable);
}
- private void initNT(NetworkTable newTable) {
- tableInstance = newTable.getInstance();
- ntPipelineEntry = newTable.getEntry("pipeline");
- ntDriverModeEntry = newTable.getEntry("driver_mode");
- ntPitchEntry = newTable.getEntry("pitch");
- ntYawEntry = newTable.getEntry("yaw");
- ntAreaEntry = newTable.getEntry("area");
- ntLatencyEntry = newTable.getEntry("latency");
- ntValidEntry = newTable.getEntry("is_valid");
- ntAuxListEntry = newTable.getEntry("aux_targets");
- ntPoseEntry = newTable.getEntry("pose");
+ private void initNT(NetworkTable camTable) {
+ tableInstance = camTable.getInstance();
+ ntPipelineEntry = camTable.getEntry("pipeline");
+ ntDriverModeEntry = camTable.getEntry("driverMode");
+ ntPitchEntry = camTable.getEntry("targetPitch");
+ ntYawEntry = camTable.getEntry("targetYaw");
+ ntAreaEntry = camTable.getEntry("targetArea");
+ ntLatencyEntry = camTable.getEntry("latency");
+ ntValidEntry = camTable.getEntry("isValid");
+ ntAuxListEntry = camTable.getEntry("auxTargets");
+ ntPoseEntry = camTable.getEntry("targetPose");
+ ntFittedHeightEntry = camTable.getEntry("targetFittedHeight");
+ ntFittedWidthEntry = camTable.getEntry("targetFittedWidth");
+ ntBoundingHeightEntry = camTable.getEntry("targetBoundingHeight");
+ ntBoundingWidthEntry = camTable.getEntry("targetBoundingWidth");
+ ntTargetRotation = camTable.getEntry("targetRotation");
ntDriveModeListenerID = ntDriverModeEntry.addListener(this::setDriverMode, EntryListenerFlags.kUpdate);
ntPipelineListenerID = ntPipelineEntry.addListener(this::setPipeline, EntryListenerFlags.kUpdate);
ntDriverModeEntry.setBoolean(false);
@@ -157,7 +166,6 @@ public class VisionProcess {
}
public void setDriverModeEntry(boolean isDriverMode) {
-
// if it's null, we haven't even started the program yet, so just return
// otherwise, set it.
if (ntDriverModeEntry != null) {
@@ -176,7 +184,7 @@ public class VisionProcess {
HashMap WebSend = new HashMap<>();
HashMap point = new HashMap<>();
HashMap pointMap = new HashMap<>();
- ArrayList