diff --git a/Main/chameleon-vision.iml b/Main/chameleon-vision.iml
index d285b15a4..f29e8915e 100644
--- a/Main/chameleon-vision.iml
+++ b/Main/chameleon-vision.iml
@@ -10,23 +10,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -66,13 +56,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/Main/pom.xml b/Main/pom.xml
index 14fd310b4..0d9b904d1 100644
--- a/Main/pom.xml
+++ b/Main/pom.xml
@@ -5,7 +5,7 @@
4.0.0
org.chameleon-vision.main
chameleon-vision
- 1.1.0-BETA
+ 1.1.2-BETA
@@ -116,37 +116,37 @@
edu.wpi.first.cscore
cscore-java
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
edu.wpi.first.cscore
cscore-jni
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
linuxaarch64bionic
edu.wpi.first.cscore
cscore-jni
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
linuxraspbian
edu.wpi.first.cscore
cscore-jni
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
linuxx86-64
edu.wpi.first.cscore
cscore-jni
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
osxx86-64
edu.wpi.first.cscore
cscore-jni
- 2019.4.1-200-g2271570
+ 2019.4.1-213-g56d782b
windowsx86-64
@@ -154,39 +154,39 @@
edu.wpi.first.cameraserver
cameraserver-java
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
edu.wpi.first.ntcore
ntcore-java
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
edu.wpi.first.ntcore
ntcore-jni
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
osxx86-64
edu.wpi.first.ntcore
ntcore-jni
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
linuxraspbian
edu.wpi.first.ntcore
ntcore-jni
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
linuxx86-64
edu.wpi.first.ntcore
ntcore-jni
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
windowsx86-64
@@ -194,7 +194,7 @@
edu.wpi.first.wpiutil
wpiutil-java
- 2019.4.1-176-ga5650b9
+ 2019.4.1-213-g56d782b
diff --git a/Main/src/main/java/com/chameleonvision/Main.java b/Main/src/main/java/com/chameleonvision/Main.java
index 3b2674dba..ead5e6834 100644
--- a/Main/src/main/java/com/chameleonvision/Main.java
+++ b/Main/src/main/java/com/chameleonvision/Main.java
@@ -1,15 +1,18 @@
package com.chameleonvision;
+import com.chameleonvision.network.NetworkManager;
+import com.chameleonvision.settings.Platform;
import com.chameleonvision.settings.SettingsManager;
import com.chameleonvision.util.Utilities;
import com.chameleonvision.vision.camera.CameraManager;
-import com.chameleonvision.vision.process.VisionProcess;
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;
public class Main {
@@ -17,14 +20,31 @@ public class Main {
private static final String NT_SERVERMODE_KEY = "--nt-servermode"; // no args for this setting
private static final String NT_CLIENTMODESERVER_KEY = "--nt-client-server"; // expects String representing an IP address (hostnames will be rejected!)
private static final String NETWORK_MANAGE_KEY = "--unmanage-network"; // no args for this setting
+ private static final String IGNORE_ROOT = "--ignore-root"; // no args for this setting
private static final int DEFAULT_PORT = 8888;
private static int webserverPort = DEFAULT_PORT;
private static boolean ntServerMode = false;
private static boolean manageNetwork = true;
+ private static boolean ignoreRoot = false;
private static String ntClientModeServer = null;
+ 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;
+ }
+ }
+ }
+
+ public static final Platform CurrentPlatform = Platform.getCurrentPlatform();
+
private static void handleArgs(String[] args) {
for (int i = 0; i < args.length; i++) {
var key = args[i].toLowerCase();
@@ -43,6 +63,7 @@ public class Main {
break;
case NT_SERVERMODE_KEY:
case NETWORK_MANAGE_KEY:
+ case IGNORE_ROOT:
// nothing
}
@@ -77,29 +98,46 @@ public class Main {
case NETWORK_MANAGE_KEY:
manageNetwork = false;
break;
+ case IGNORE_ROOT:
+ ignoreRoot = true;
}
}
}
public static void main(String[] args) {
handleArgs(args);
+
+ if (!CurrentPlatform.isRoot()) {
+ if (ignoreRoot) {
+ // TODO: should we do this?
+ // manageNetwork = false;
+ System.out.println("Ignoring root, network will not be managed!");
+ } else {
+ System.err.println("This program must be run as root!");
+ return;
+ }
+ }
+
// Attempt to load the JNI Libraries
try {
+ if (CurrentPlatform.equals(Platform.LINUX_ARM64))
CameraServerJNI.forceLoad();
CameraServerCvJNI.forceLoad();
} catch (IOException e) {
- var errorStr = SettingsManager.getCurrentPlatform().equals(SettingsManager.Platform.UNSUPPORTED) ? "Unsupported platform!" : "Failed to load JNI Libraries!";
+ var errorStr = CurrentPlatform.equals(Platform.UNSUPPORTED) ? "Unsupported platform!" : "Failed to load JNI Libraries!";
throw new RuntimeException(errorStr);
}
if (CameraManager.initializeCameras()) {
- SettingsManager.initialize(manageNetwork);
+ SettingsManager.initialize();
+ NetworkManager.initialize(manageNetwork);
CameraManager.initializeThreads();
if (ntServerMode) {
System.out.println("Starting NT Server");
NetworkTableInstance.getDefault().startServer();
} else {
+ NetworkTableInstance.getDefault().addLogger(new NTLogger(), 0, 255); // to hide error messages
if (ntClientModeServer != null) {
NetworkTableInstance.getDefault().startClient(ntClientModeServer);
} else {
diff --git a/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java b/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java
new file mode 100644
index 000000000..3e1d811de
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java
@@ -0,0 +1,82 @@
+package com.chameleonvision.network;
+
+import com.chameleonvision.settings.NetworkSettings;
+import com.chameleonvision.settings.SettingsManager;
+import com.chameleonvision.util.ShellExec;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LinuxNetworking extends SysNetworking {
+
+ private ShellExec shell = new ShellExec(true, true);
+
+ @Override
+ public boolean setDHCP() {
+ String[] clearArgs = { "addr", "flush", "dev", networkInterface.name };
+ try {
+ int clearRetCode = shell.execute("ip", clearArgs);
+ int dhcpRetCode = shell.execute("dhclient", networkInterface.name);
+ return clearRetCode == 0 && dhcpRetCode == 0;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setHostname(String newHostname) {
+ String[] setHostnameArgs = { "set-hostname", newHostname };
+ try {
+ var setHostnameRetCode = shell.execute("hostnamectl", setHostnameArgs);
+ return setHostnameRetCode == 0;
+ } catch(Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast) {
+ try {
+ String[] clearArgs = { "addr", "flush", "dev", networkInterface.name };
+ String[] setIPArgs = { "addr", "add", String.format("%s/%s", ipAddress, netmask), "broadcast", broadcast, "dev", networkInterface.name };
+ String[] setGatewayArgs = { "route", "replace", "default", "via", gateway, "dev", networkInterface.name };
+
+ int clearRetCode = shell.execute("ip", clearArgs);
+ int setIPRetCode = shell.execute("ip", setIPArgs);
+ int setGatewayRetCode = shell.execute("ip", setGatewayArgs);
+
+ return clearRetCode == 0 && setIPRetCode == 0 && setGatewayRetCode == 0;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public List getNetworkInterfaces() throws SocketException {
+ List netInterfaces;
+ try {
+ netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces());
+ } catch (SocketException e) {
+ return null;
+ }
+
+ List goodInterfaces = new ArrayList<>();
+
+ for (var netInterface : netInterfaces) {
+ if (netInterface.getDisplayName().contains("lo")) continue;
+ if (!netInterface.isUp()) continue;
+ goodInterfaces.add(netInterface);
+ }
+ return goodInterfaces;
+
+ }
+}
diff --git a/Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java b/Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java
new file mode 100644
index 000000000..ec5a376d3
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java
@@ -0,0 +1,7 @@
+package com.chameleonvision.network;
+
+public enum NetworkIPMode {
+ DHCP,
+ STATIC,
+ UNKNOWN
+}
diff --git a/Main/src/main/java/com/chameleonvision/network/NetworkInterface.java b/Main/src/main/java/com/chameleonvision/network/NetworkInterface.java
new file mode 100644
index 000000000..75b5f7224
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/NetworkInterface.java
@@ -0,0 +1,57 @@
+package com.chameleonvision.network;
+
+import com.chameleonvision.settings.GeneralSettings;
+
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+
+public class NetworkInterface {
+ public final String name;
+ public final String displayName;
+ public final String IPAddress;
+ public final String Netmask;
+ public final String Gateway;
+ public final String Broadcast;
+
+ public NetworkInterface(java.net.NetworkInterface inetface, InterfaceAddress ifaceAddress) {
+ name = inetface.getName();
+ displayName = inetface.getDisplayName();
+
+ var inetAddress = ifaceAddress.getAddress();
+ IPAddress = inetAddress.getHostAddress();
+ Netmask = getIPv4LocalNetMask(ifaceAddress);
+
+ // TODO: hack to "get" gateway, this is gross and bad, pls fix
+ var splitIPAddr = IPAddress.split("\\.");
+ splitIPAddr[3] = "1";
+ Gateway = String.join(".", splitIPAddr);
+ splitIPAddr[3] = "255";
+ Broadcast = String.join(".", splitIPAddr);
+ }
+
+ private static String getIPv4LocalNetMask(InterfaceAddress interfaceAddress) {
+ var netPrefix = interfaceAddress.getNetworkPrefixLength();
+ try {
+ // Since this is for IPv4, it's 32 bits, so set the sign value of
+ // the int to "negative"...
+ int shiftby = (1<<31);
+ // For the number of bits of the prefix -1 (we already set the sign bit)
+ for (int i = netPrefix - 1; i > 0; i--) {
+ // Shift the sign right... Java makes the sign bit sticky on a shift...
+ // So no need to "set it back up"...
+ shiftby = (shiftby >> 1);
+ }
+ // Transform the resulting value in xxx.xxx.xxx.xxx format, like if
+ /// it was a standard address...
+ String maskString = ((shiftby >> 24) & 255) + "." + ((shiftby >> 16) & 255) + "." + ((shiftby >> 8) & 255) + "." + (shiftby & 255);
+ // Return the address thus created...
+ return maskString;
+// return InetAddress.getByName(maskString);
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ // Something went wrong here...
+ return null;
+ }
+}
diff --git a/Main/src/main/java/com/chameleonvision/network/NetworkManager.java b/Main/src/main/java/com/chameleonvision/network/NetworkManager.java
new file mode 100644
index 000000000..294d041e6
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/NetworkManager.java
@@ -0,0 +1,122 @@
+package com.chameleonvision.network;
+
+
+import com.chameleonvision.settings.NetworkSettings;
+import com.chameleonvision.settings.Platform;
+import com.chameleonvision.settings.SettingsManager;
+
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class NetworkManager {
+ private NetworkManager() {}
+
+ protected static SysNetworking networking;
+ protected static NetworkInterface botInterface = null;
+ private static boolean isManaged = false;
+
+ public static void initialize(boolean manage) {
+ isManaged = manage;
+ if (!isManaged) {
+ return;
+ }
+
+ Platform platform = Platform.getCurrentPlatform();
+
+ if (platform.isLinux()) {
+ networking = new LinuxNetworking();
+ } else if (platform.isWindows()) {
+// networking = new WindowsNetworking();
+ System.out.println("Windows networking is not yet supported. Running unmanaged.");
+ return;
+ }
+
+ List interfaces = new ArrayList<>();
+ List goodInterfaces = new ArrayList<>();
+
+ try {
+ interfaces = networking.getNetworkInterfaces();
+ } catch (SocketException e) {
+ e.printStackTrace();
+ }
+
+ var teamBytes = NetworkSettings.GetTeamNumberIPBytes(SettingsManager.GeneralSettings.team_number);
+
+ if (interfaces.size() > 0) {
+ for (var inetface : interfaces) {
+ for (var inetfaceAddr : inetface.getInterfaceAddresses()) {
+ var rawAddr = inetfaceAddr.getAddress().getAddress();
+ if (rawAddr.length > 4) continue;
+ if (rawAddr[1] == teamBytes[0] && rawAddr[2] == teamBytes[1]) {
+ goodInterfaces.add(new NetworkInterface(inetface, inetfaceAddr));
+ }
+ }
+ }
+
+ if (goodInterfaces.size() == 0) {
+ isManaged = false;
+ System.err.println("No valid network interfaces found! Staying unmanaged.");
+ return;
+ }
+
+ botInterface = goodInterfaces.get(0);
+ networking.setNetworkInterface(botInterface);
+ } else {
+ isManaged = false;
+ System.err.println("No valid network interfaces found! Staying unmanaged.");
+ return;
+ }
+
+ if(!loadFromGeneralSettings()) {
+ isManaged = false;
+ System.err.println("Failed to load network settings. Staying unmanaged!");
+ }
+ }
+
+ private static boolean loadFromGeneralSettings() {
+ if (!isManaged) {
+ return true;
+ }
+
+ var genSettings = SettingsManager.GeneralSettings;
+ boolean isStatic = genSettings.connection_type.toLowerCase().equals("static");
+
+ if (isStatic) {
+ var splitIPAddr = genSettings.ip.split("\\.");
+ splitIPAddr[3] = "255";
+ var broadcast = String.join(".", splitIPAddr);
+ if (!setStatic(genSettings.ip, genSettings.netmask, genSettings.gateway, broadcast)) {
+ return false;
+ }
+ } else {
+ if (!setDHCP()) {
+ return false;
+ }
+ }
+
+ return setHostname(genSettings.hostname);
+ }
+
+ private static boolean setDHCP() {
+ if (!isManaged) {
+ return true;
+ }
+ return networking.setDHCP();
+ }
+
+ private static boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast) {
+ if (!isManaged) {
+ return true;
+ }
+ return networking.setStatic(ipAddress, netmask, gateway, broadcast);
+ }
+
+ private static boolean setHostname(String hostname) {
+ if (!isManaged) {
+ return true;
+ }
+ return networking.setHostname(hostname);
+ }
+}
diff --git a/Main/src/main/java/com/chameleonvision/network/SysNetworking.java b/Main/src/main/java/com/chameleonvision/network/SysNetworking.java
new file mode 100644
index 000000000..91684e775
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/SysNetworking.java
@@ -0,0 +1,37 @@
+package com.chameleonvision.network;
+
+import com.chameleonvision.util.ShellExec;
+
+import java.io.IOException;
+import java.net.SocketException;
+import java.util.List;
+import java.util.Scanner;
+
+public abstract class SysNetworking {
+
+ NetworkInterface networkInterface;
+ ShellExec shell = new ShellExec(true, true);
+
+ public String getHostname() {
+ try {
+ var retCode = shell.execute("hostname", null, true);
+ if (retCode == 0) {
+ while(!shell.isOutputCompleted()) {}
+ return shell.getOutput();
+ } else {
+ return null;
+ }
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public void setNetworkInterface(NetworkInterface networkInterface) {
+ this.networkInterface = networkInterface;
+ }
+ public abstract boolean setDHCP();
+ public abstract boolean setHostname(String hostname);
+ public abstract boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast);
+ public abstract List getNetworkInterfaces() throws SocketException;
+
+}
diff --git a/Main/src/main/java/com/chameleonvision/network/WindowsNetworking.java b/Main/src/main/java/com/chameleonvision/network/WindowsNetworking.java
new file mode 100644
index 000000000..d6ccd829b
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/network/WindowsNetworking.java
@@ -0,0 +1,57 @@
+package com.chameleonvision.network;
+
+import com.chameleonvision.settings.NetworkSettings;
+import com.chameleonvision.settings.SettingsManager;
+
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class WindowsNetworking extends SysNetworking {
+
+ @Override
+ public boolean setDHCP() {
+ return false;
+ }
+
+ @Override
+ public boolean setHostname(String newHostname) {
+ var currentHostname = getHostname();
+
+ if (getHostname() == null) {
+ return false;
+ }
+
+ String command = String.format("wmic computersystem where name=\"%s\" call rename name=\"%s\"", currentHostname, newHostname);
+
+ try {
+ var process = Runtime.getRuntime().exec(command);
+ var returnCode = process.waitFor();
+ return returnCode == 0;
+ } catch(Exception e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast) {
+ return false;
+ }
+
+ @Override
+ public List getNetworkInterfaces() throws SocketException {
+ var netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces());
+
+ List goodInterfaces = new ArrayList<>();
+
+ for (var netInterface : netInterfaces) {
+ if (netInterface.getDisplayName().toLowerCase().contains("bluetooth")) continue;
+ if (netInterface.getDisplayName().toLowerCase().contains("virtual")) continue;
+ if (netInterface.getDisplayName().toLowerCase().contains("loopback")) continue;
+ if (!netInterface.isUp()) continue;
+ goodInterfaces.add(netInterface);
+ }
+ return goodInterfaces;
+ }
+}
diff --git a/Main/src/main/java/com/chameleonvision/vision/GeneralSettings.java b/Main/src/main/java/com/chameleonvision/settings/GeneralSettings.java
similarity index 88%
rename from Main/src/main/java/com/chameleonvision/vision/GeneralSettings.java
rename to Main/src/main/java/com/chameleonvision/settings/GeneralSettings.java
index 4ef6ab20b..f921af331 100644
--- a/Main/src/main/java/com/chameleonvision/vision/GeneralSettings.java
+++ b/Main/src/main/java/com/chameleonvision/settings/GeneralSettings.java
@@ -1,4 +1,4 @@
-package com.chameleonvision.vision;
+package com.chameleonvision.settings;
public class GeneralSettings {
public int team_number = 1577;
diff --git a/Main/src/main/java/com/chameleonvision/settings/NetworkSettings.java b/Main/src/main/java/com/chameleonvision/settings/NetworkSettings.java
index 75e289e87..78c28e017 100644
--- a/Main/src/main/java/com/chameleonvision/settings/NetworkSettings.java
+++ b/Main/src/main/java/com/chameleonvision/settings/NetworkSettings.java
@@ -1,9 +1,12 @@
package com.chameleonvision.settings;
import java.net.*;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import com.chameleonvision.util.Utilities;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
public class NetworkSettings {
@@ -51,20 +54,26 @@ public class NetworkSettings {
}
}
+ public static byte[] GetTeamNumberIPBytes(int teamNumber) {
+ return new byte[]{(byte) (teamNumber / 100), (byte) (teamNumber % 100)};
+ }
+
public static String getAdapter() {
try {//TODO fix windows get adapter
Enumeration nets = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface netint : Collections.list(nets)) {
Enumeration ee = netint.getInetAddresses();
for (InetAddress addr : Collections.list(ee))
- if (addr instanceof Inet4Address)
- if ((addr.getAddress()[0] & 0xFF) == 192 && (addr.getAddress()[1] & 0xFF) == 168) {
+ if (addr instanceof Inet4Address) {
+ var addrString = addr.toString();
+ if ((addr.getAddress()[0] & 0xFF) == 10 && (addr.getAddress()[1] & 0xFF) == 168) {
System.out.println("found robot network interface at " + netint.getName() + " ip: " + addr.getHostAddress());
return netint.getName();
}
+ }
}
} catch (SocketException e) {
- System.err.println("Socket exception while trying to find current ip");
+ System.err.println("Socket exception while trying to find current IP");
}
return "";
}
diff --git a/Main/src/main/java/com/chameleonvision/settings/Platform.java b/Main/src/main/java/com/chameleonvision/settings/Platform.java
new file mode 100644
index 000000000..b4a4daf55
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/settings/Platform.java
@@ -0,0 +1,81 @@
+package com.chameleonvision.settings;
+
+import com.chameleonvision.util.ShellExec;
+
+import java.io.IOException;
+
+public enum Platform {
+ WINDOWS_64("Windows x64"),
+ LINUX_64("Linux x64"),
+ LINUX_RASPBIAN("Linux Raspbian"),
+ LINUX_TEGRA("Linux For Tegra"),
+ LINUX_ARM64("Linux ARM64"),
+ MACOS_64("Mac OS x64"),
+ UNSUPPORTED("Unsupported Platform");
+
+ public final String value;
+
+ Platform(String value) {
+ this.value = value;
+ }
+
+ public boolean isWindows() {
+ return this == WINDOWS_64;
+ }
+
+ public boolean isLinux() {
+ return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_ARM64 || this == LINUX_TEGRA;
+ }
+
+ public boolean isMac() {
+ return this == MACOS_64;
+ }
+
+ private static ShellExec shell = new ShellExec(true, false);
+
+ public boolean isRoot() {
+ if (isLinux() || isMac()) {
+ try {
+ shell.execute("id", null, true, "-u");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ while (!shell.isOutputCompleted()) {}
+ if (shell.getExitCode() == 0) {
+ var out = shell.getOutput();
+ out = out.split("\n")[0];
+ return out.equals("0");
+ }
+ } else if (isWindows()) {
+ return true;
+ } else {
+ return true;
+ }
+ return false;
+ }
+
+ public static Platform getCurrentPlatform() {
+ var osName = System.getProperty("os.name");
+ var osArch = System.getProperty("os.arch");
+
+ if (osName.contains("Windows")) {
+ if (osArch.equals("amd64")) return Platform.WINDOWS_64;
+ return Platform.UNSUPPORTED;
+ }
+
+ if (osName.contains("Linux")) {
+ if (osName.contains("Tegra")) return Platform.LINUX_TEGRA;
+ if (osArch.equals("amd64")) return Platform.LINUX_64;
+ if (osArch.contains("rasp")) return Platform.LINUX_RASPBIAN;
+ if (osArch.contains("aarch")) return Platform.LINUX_ARM64;
+ return Platform.UNSUPPORTED;
+ }
+
+ if (osName.contains("Mac")) {
+ if (osArch.equals("amd64")) return Platform.MACOS_64;
+ return Platform.UNSUPPORTED;
+ }
+
+ return Platform.UNSUPPORTED;
+ }
+}
diff --git a/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java b/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java
index 221b2974e..6a2a3c310 100644
--- a/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java
+++ b/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java
@@ -1,7 +1,7 @@
package com.chameleonvision.settings;
-import com.chameleonvision.FileHelper;
-import com.chameleonvision.vision.GeneralSettings;
+import com.chameleonvision.network.NetworkManager;
+import com.chameleonvision.util.FileHelper;
import com.chameleonvision.vision.camera.CameraManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -15,21 +15,22 @@ import java.nio.file.Paths;
public class SettingsManager {
public static final Path SettingsPath = Paths.get(System.getProperty("user.dir"), "settings");
- public static com.chameleonvision.vision.GeneralSettings GeneralSettings;
+ public static com.chameleonvision.settings.GeneralSettings GeneralSettings;
private SettingsManager() {}
- public static void initialize(boolean manageNetwork) {
+ public static void initialize() {
initGeneralSettings();
- if (manageNetwork) {
- NetworkSettings netSettings = new NetworkSettings();
- netSettings.hostname = GeneralSettings.hostname;
- netSettings.gateway = GeneralSettings.gateway;
- netSettings.netmask = GeneralSettings.netmask;
- netSettings.connectionType = GeneralSettings.connection_type;
- netSettings.ip = GeneralSettings.ip;
- netSettings.run();
- }
+// if (manageNetwork) {
+
+// NetworkSettings netSettings = new NetworkSettings();
+// netSettings.hostname = GeneralSettings.hostname;
+// netSettings.gateway = GeneralSettings.gateway;
+// netSettings.netmask = GeneralSettings.netmask;
+// netSettings.connectionType = GeneralSettings.connection_type;
+// netSettings.ip = GeneralSettings.ip;
+// netSettings.run();
+// }
var allCameras = CameraManager.getAllCamerasByName();
if (!allCameras.containsKey(GeneralSettings.curr_camera) && allCameras.size() > 0) {
var cam = allCameras.entrySet().stream().findFirst().get().getValue();
@@ -38,49 +39,10 @@ public class SettingsManager {
}
}
- public enum Platform {
- WINDOWS_64("Windows x64"),
- LINUX_64("Linux x64"),
- LINUX_RASPBIAN("Linux Raspbian"),
- LINUX_AARCH64("Linux ARM 64bit"),
- MACOS_64("Mac OS x64"),
- UNSUPPORTED("Unsupported Platform");
-
- public final String value;
-
- Platform(String value) {
- this.value = value;
- }
- }
-
- public static Platform getCurrentPlatform() {
- var osName = System.getProperty("os.name");
- var osArch = System.getProperty("os.arch");
-
- if (osName.contains("Windows")) {
- if (osArch.equals("amd64")) return Platform.WINDOWS_64;
- return Platform.UNSUPPORTED;
- }
-
- if (osName.contains("Linux")) {
- if (osArch.equals("amd64")) return Platform.LINUX_64;
- if (osArch.contains("rasp")) return Platform.LINUX_RASPBIAN;
- if (osArch.contains("aarch")) return Platform.LINUX_64;
- return Platform.UNSUPPORTED;
- }
-
- if (osName.contains("Mac")) {
- if (osArch.equals("amd64")) return Platform.MACOS_64;
- return Platform.UNSUPPORTED;
- }
-
- return Platform.UNSUPPORTED;
- }
-
private static void initGeneralSettings() {
FileHelper.CheckPath(SettingsPath);
try {
- GeneralSettings = new Gson().fromJson(new FileReader(Paths.get(SettingsPath.toString(), "settings.json").toString()), com.chameleonvision.vision.GeneralSettings.class);
+ GeneralSettings = new Gson().fromJson(new FileReader(Paths.get(SettingsPath.toString(), "settings.json").toString()), com.chameleonvision.settings.GeneralSettings.class);
} catch (FileNotFoundException e) {
GeneralSettings = new GeneralSettings();
}
diff --git a/Main/src/main/java/com/chameleonvision/FileHelper.java b/Main/src/main/java/com/chameleonvision/util/FileHelper.java
similarity index 94%
rename from Main/src/main/java/com/chameleonvision/FileHelper.java
rename to Main/src/main/java/com/chameleonvision/util/FileHelper.java
index 8ddb39b05..8f4628cbe 100644
--- a/Main/src/main/java/com/chameleonvision/FileHelper.java
+++ b/Main/src/main/java/com/chameleonvision/util/FileHelper.java
@@ -1,4 +1,4 @@
-package com.chameleonvision;
+package com.chameleonvision.util;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/Main/src/main/java/com/chameleonvision/Handler/MathHandler.java b/Main/src/main/java/com/chameleonvision/util/MathHandler.java
similarity index 80%
rename from Main/src/main/java/com/chameleonvision/Handler/MathHandler.java
rename to Main/src/main/java/com/chameleonvision/util/MathHandler.java
index fd2c4edbb..35c634c26 100644
--- a/Main/src/main/java/com/chameleonvision/Handler/MathHandler.java
+++ b/Main/src/main/java/com/chameleonvision/util/MathHandler.java
@@ -1,17 +1,20 @@
-package com.chameleonvision.Handler;
-import org.apache.commons.math3.util.FastMath;
+package com.chameleonvision.util;
import java.lang.Math;
+
public class MathHandler {
- MathHandler(){}
+ MathHandler() {}
+
public static double sigmoid(double x){
double bias = 0;
double a = 5;
double b = -0.05;
double k = 200;
+
if (x < 50){
bias = -1.338;
}
+
return ((k / (1 + Math.pow(Math.E,(a + (b * x))))) + bias);
}
public static double toSlope(double angle){
diff --git a/Main/src/main/java/com/chameleonvision/MemoryManager.java b/Main/src/main/java/com/chameleonvision/util/MemoryManager.java
similarity index 96%
rename from Main/src/main/java/com/chameleonvision/MemoryManager.java
rename to Main/src/main/java/com/chameleonvision/util/MemoryManager.java
index ef6736e42..20bfd0425 100644
--- a/Main/src/main/java/com/chameleonvision/MemoryManager.java
+++ b/Main/src/main/java/com/chameleonvision/util/MemoryManager.java
@@ -1,4 +1,4 @@
-package com.chameleonvision;
+package com.chameleonvision.util;
public class MemoryManager {
diff --git a/Main/src/main/java/com/chameleonvision/util/ShellExec.java b/Main/src/main/java/com/chameleonvision/util/ShellExec.java
new file mode 100644
index 000000000..a6347edba
--- /dev/null
+++ b/Main/src/main/java/com/chameleonvision/util/ShellExec.java
@@ -0,0 +1,151 @@
+package com.chameleonvision.util;
+
+import java.io.*;
+
+/**
+ * Execute external process and optionally read output buffer.
+ */
+public class ShellExec {
+ private int exitCode;
+ private boolean readOutput, readError;
+ private StreamGobbler errorGobbler, outputGobbler;
+
+ public ShellExec() {
+ this(false, false);
+ }
+
+ public ShellExec(boolean readOutput, boolean readError) {
+ this.readOutput = readOutput;
+ this.readError = readError;
+ }
+
+ /**
+ * 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")
+ * @param args 0..n command line arguments
+ * @return process exit code
+ */
+ public int execute(String command, String... args) throws IOException {
+ return execute(command, null, true, args);
+ }
+
+ /**
+ * Execute a command.
+ * @param command command ("c:/some/folder/script.bat" or "some/folder/script.sh")
+ * @param workdir working directory or NULL to use command folder
+ * @param wait wait for process to end
+ * @param args 0..n command line arguments
+ * @return process exit code
+ */
+ public int execute(String command, String workdir, boolean wait, String...args) throws IOException {
+ String[] cmdArr;
+ if (args != null && args.length > 0) {
+ cmdArr = new String[1+args.length];
+ cmdArr[0] = command;
+ System.arraycopy(args, 0, cmdArr, 1, args.length);
+ } else {
+ cmdArr = new String[] { command };
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(cmdArr);
+ File workingDir = (workdir==null ? new File(command).getParentFile() : new File(workdir) );
+ pb.directory(workingDir);
+
+ Process process = pb.start();
+
+ // 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;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public boolean isOutputCompleted() {
+ return (outputGobbler != null && outputGobbler.isCompleted());
+ }
+
+ public boolean isErrorCompleted() {
+ return (errorGobbler != null && errorGobbler.isCompleted());
+ }
+
+ public String getOutput() {
+ return (outputGobbler != null ? outputGobbler.getOutput() : null);
+ }
+
+ public String getError() {
+ return (errorGobbler != null ? errorGobbler.getOutput() : null);
+ }
+
+//********************************************
+//********************************************
+
+ /**
+ * StreamGobbler reads inputstream to "gobble" it.
+ * This is used by Executor class when running
+ * a commandline applications. Gobblers must read/purge
+ * INSTR and ERRSTR process streams.
+ * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
+ */
+ @SuppressWarnings("WeakerAccess")
+ private class StreamGobbler extends Thread {
+ private InputStream is;
+ private StringBuilder output;
+ private volatile boolean completed; // mark volatile to guarantee a thread safety
+
+ public StreamGobbler(InputStream is, boolean readStream) {
+ this.is = is;
+ this.output = (readStream ? new StringBuilder(256) : null);
+ }
+
+ public void run() {
+ completed = false;
+ try {
+ String NL = System.getProperty("line.separator", "\r\n");
+
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line;
+ while ( (line = br.readLine()) != null) {
+ if (output != null)
+ output.append(line).append(NL);
+ }
+ } catch (IOException ex) {
+ // ex.printStackTrace();
+ }
+ completed = true;
+ }
+
+ /**
+ * Get inputstream buffer or null if stream
+ * was not consumed.
+ * @return
+ */
+ public String getOutput() {
+ return (output != null ? output.toString() : null);
+ }
+
+ /**
+ * Is input stream completed.
+ * @return
+ */
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ }
+
+}
diff --git a/Main/src/main/java/com/chameleonvision/util/Utilities.java b/Main/src/main/java/com/chameleonvision/util/Utilities.java
index 358559643..800a80330 100644
--- a/Main/src/main/java/com/chameleonvision/util/Utilities.java
+++ b/Main/src/main/java/com/chameleonvision/util/Utilities.java
@@ -1,5 +1,8 @@
package com.chameleonvision.util;
+import java.util.ArrayList;
+import java.util.List;
+
public class Utilities {
private Utilities() {}
@@ -8,4 +11,30 @@ public class Utilities {
return ip.matches(PATTERN);
}
+
+ public static List getDigitBytes(int num) {
+ List digits = new ArrayList<>();
+ collectDigitBytes(num, digits);
+ return digits;
+ }
+
+ private static void collectDigitBytes(int num, List digits) {
+ if (num / 10 > 0) {
+ collectDigitBytes( num / 10, digits);
+ }
+ digits.add((byte) (num % 10));
+ }
+
+ public static List getDigits(int num) {
+ List digits = new ArrayList();
+ collectDigits(num, digits);
+ return digits;
+ }
+
+ private static void collectDigits(int num, List digits) {
+ if(num / 10 > 0) {
+ collectDigits(num / 10, digits);
+ }
+ digits.add(num % 10);
+ }
}
diff --git a/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java b/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java
index 52be245e4..23038d26d 100644
--- a/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java
+++ b/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java
@@ -1,13 +1,11 @@
package com.chameleonvision.vision.camera;
-import com.chameleonvision.CameraException;
-import com.chameleonvision.settings.SettingsManager;
+import com.chameleonvision.settings.Platform;
import com.chameleonvision.vision.Pipeline;
import com.chameleonvision.web.ServerHandler;
import edu.wpi.cscore.*;
import edu.wpi.first.cameraserver.CameraServer;
import org.opencv.core.Mat;
-import org.springframework.core.env.Environment;
import java.util.Arrays;
import java.util.HashMap;
@@ -18,7 +16,7 @@ public class Camera {
private static final double DEFAULT_FOV = 60.8;
private static final int MINIMUM_FPS = 30;
private static final int MINIMUM_WIDTH = 320;
- private static final int MINIMUM_HEIGHT = 240;
+ private static final int MINIMUM_HEIGHT = 200;
private static final int MAX_INIT_MS = 1500;
public final String name;
@@ -67,12 +65,11 @@ public class Camera {
this.pipelines = pipelines;
// set up video modes according to minimums
- if (SettingsManager.getCurrentPlatform() == SettingsManager.Platform.WINDOWS_64 && !UsbCam.isConnected()) {
+ if (Platform.getCurrentPlatform() == Platform.WINDOWS_64 && !UsbCam.isConnected()) {
System.out.print("Waiting on camera... ");
long initTimeout = System.nanoTime();
while(!UsbCam.isConnected())
{
- //TODO add a time sleep, can wait only so long before giving up
if (((System.nanoTime() - initTimeout) / 1e6 ) >= MAX_INIT_MS) {
break;
}
@@ -80,7 +77,8 @@ public class Camera {
var initTimeMs = (System.nanoTime() - initTimeout) / 1e6;
System.out.printf("Camera initialized in %.2fms\n", initTimeMs);
}
- availableVideoModes = UsbCam.enumerateVideoModes();
+ var trueVideoModes = UsbCam.enumerateVideoModes();
+ availableVideoModes = Arrays.stream(trueVideoModes).filter(v -> v.fps >= MINIMUM_FPS && v.width >= MINIMUM_WIDTH && v.height >= MINIMUM_HEIGHT).toArray(VideoMode[]::new);
if (availableVideoModes.length == 0) {
System.err.println("Camera not supported!");
throw new RuntimeException(new CameraException(CameraException.CameraExceptionType.BAD_CAMERA));
diff --git a/Main/src/main/java/com/chameleonvision/CameraException.java b/Main/src/main/java/com/chameleonvision/vision/camera/CameraException.java
similarity index 86%
rename from Main/src/main/java/com/chameleonvision/CameraException.java
rename to Main/src/main/java/com/chameleonvision/vision/camera/CameraException.java
index 6be5ec7a9..f4c5671e3 100644
--- a/Main/src/main/java/com/chameleonvision/CameraException.java
+++ b/Main/src/main/java/com/chameleonvision/vision/camera/CameraException.java
@@ -1,4 +1,4 @@
-package com.chameleonvision;
+package com.chameleonvision.vision.camera;
public class CameraException extends Exception {
public enum CameraExceptionType {
@@ -19,7 +19,7 @@ public class CameraException extends Exception {
}
}
- public CameraException(CameraExceptionType camExceptionType) {
+ CameraException(CameraExceptionType camExceptionType) {
super(camExceptionType.toString());
}
}
diff --git a/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java b/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java
index 501a7c771..7a17c7ef9 100644
--- a/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java
+++ b/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java
@@ -1,9 +1,7 @@
package com.chameleonvision.vision.camera;
-import com.chameleonvision.CameraException;
-import com.chameleonvision.FileHelper;
+import com.chameleonvision.util.FileHelper;
import com.chameleonvision.settings.SettingsManager;
-import com.chameleonvision.vision.GeneralSettings;
import com.chameleonvision.vision.Pipeline;
import com.chameleonvision.vision.process.VisionProcess;
import com.google.gson.Gson;
@@ -15,7 +13,6 @@ import org.opencv.videoio.VideoCapture;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
diff --git a/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java b/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java
index 007e52166..4ced66d97 100644
--- a/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java
+++ b/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java
@@ -1,7 +1,7 @@
package com.chameleonvision.vision.process;
import com.chameleonvision.vision.camera.CameraValues;
-import com.chameleonvision.Handler.MathHandler;
+import com.chameleonvision.util.MathHandler;
import org.apache.commons.math3.util.FastMath;
import org.jetbrains.annotations.NotNull;
import org.opencv.core.*;
diff --git a/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java b/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java
index aa55cb1db..9a7fff80b 100644
--- a/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java
+++ b/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java
@@ -113,14 +113,20 @@ public class VisionProcess implements Runnable {
}
private void updateNetworkTables(PipelineResult pipelineResult) {
- ntValidEntry.setBoolean(pipelineResult.IsValid);
if (pipelineResult.IsValid) {
+ ntValidEntry.setBoolean(true);
ntYawEntry.setNumber(pipelineResult.Yaw);
ntPitchEntry.setNumber(pipelineResult.Pitch);
ntDistanceEntry.setNumber(pipelineResult.Area);
+ ntTimeStampEntry.setNumber(TimeStamp);
NetworkTableInstance.getDefault().flush();
+ } else {
+ ntYawEntry.setNumber(0.0);
+ ntPitchEntry.setNumber(0.0);
+ ntDistanceEntry.setNumber(0.0);
+ ntTimeStampEntry.setNumber(TimeStamp);
+ ntValidEntry.setBoolean(false);
}
- ntTimeStampEntry.setNumber(timeStamp);
}
private PipelineResult runVisionProcess(Mat inputImage, Mat outputImage) {
diff --git a/Main/src/main/java/com/chameleonvision/web/ServerHandler.java b/Main/src/main/java/com/chameleonvision/web/ServerHandler.java
index 698f5bb45..89a52c1dc 100644
--- a/Main/src/main/java/com/chameleonvision/web/ServerHandler.java
+++ b/Main/src/main/java/com/chameleonvision/web/ServerHandler.java
@@ -1,11 +1,8 @@
package com.chameleonvision.web;
-import com.chameleonvision.CameraException;
+import com.chameleonvision.vision.camera.CameraException;
import com.chameleonvision.settings.SettingsManager;
-import com.chameleonvision.vision.Pipeline;
-import com.chameleonvision.vision.camera.Camera;
import com.chameleonvision.vision.camera.CameraManager;
-import com.google.gson.JsonArray;
import edu.wpi.cscore.VideoException;
import io.javalin.websocket.WsCloseContext;
import io.javalin.websocket.WsConnectContext;