From 76997660916cdd6ce5ab7ce0e59a921fbcb59c30 Mon Sep 17 00:00:00 2001 From: Banks Troutman Date: Fri, 4 Oct 2019 15:55:45 -0400 Subject: [PATCH 1/4] Begin network rewrite --- Main/pom.xml | 16 ++-- .../main/java/com/chameleonvision/Main.java | 6 +- .../{vision => settings}/GeneralSettings.java | 2 +- .../settings/NetworkSettings.java | 15 +++- .../chameleonvision/settings/Platform.java | 52 ++++++++++++ .../settings/SettingsManager.java | 62 +++----------- .../settings/network/INetworking.java | 14 ++++ .../settings/network/LinuxNetworking.java | 37 +++++++++ .../settings/network/NetworkIPMode.java | 7 ++ .../settings/network/NetworkInterface.java | 23 +++++ .../settings/network/NetworkManager.java | 40 +++++++++ .../settings/network/WindowsNetworking.java | 83 +++++++++++++++++++ .../{ => util}/FileHelper.java | 2 +- .../{ => util}/MemoryManager.java | 2 +- .../com/chameleonvision/util/Utilities.java | 29 +++++++ .../chameleonvision/vision/camera/Camera.java | 7 +- .../{ => vision/camera}/CameraException.java | 4 +- .../vision/camera/CameraManager.java | 5 +- .../vision/process/VisionProcess.java | 10 ++- .../chameleonvision/web/ServerHandler.java | 5 +- 20 files changed, 338 insertions(+), 83 deletions(-) rename Main/src/main/java/com/chameleonvision/{vision => settings}/GeneralSettings.java (88%) create mode 100644 Main/src/main/java/com/chameleonvision/settings/Platform.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/INetworking.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java create mode 100644 Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java rename Main/src/main/java/com/chameleonvision/{ => util}/FileHelper.java (94%) rename Main/src/main/java/com/chameleonvision/{ => util}/MemoryManager.java (96%) rename Main/src/main/java/com/chameleonvision/{ => vision/camera}/CameraException.java (86%) diff --git a/Main/pom.xml b/Main/pom.xml index 14fd310b4..0449c7f57 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 @@ -154,39 +154,39 @@ edu.wpi.first.cameraserver cameraserver-java - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 edu.wpi.first.ntcore ntcore-java - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 edu.wpi.first.ntcore ntcore-jni - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 osxx86-64 edu.wpi.first.ntcore ntcore-jni - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 linuxraspbian edu.wpi.first.ntcore ntcore-jni - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 linuxx86-64 edu.wpi.first.ntcore ntcore-jni - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 windowsx86-64 @@ -194,7 +194,7 @@ edu.wpi.first.wpiutil wpiutil-java - 2019.4.1-176-ga5650b9 + 2019.4.1-200-g2271570 diff --git a/Main/src/main/java/com/chameleonvision/Main.java b/Main/src/main/java/com/chameleonvision/Main.java index 3b2674dba..e380eec0a 100644 --- a/Main/src/main/java/com/chameleonvision/Main.java +++ b/Main/src/main/java/com/chameleonvision/Main.java @@ -1,9 +1,10 @@ package com.chameleonvision; +import com.chameleonvision.settings.Platform; import com.chameleonvision.settings.SettingsManager; +import com.chameleonvision.settings.network.NetworkManager; 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; @@ -83,12 +84,13 @@ public class Main { public static void main(String[] args) { handleArgs(args); + // Attempt to load the JNI Libraries try { CameraServerJNI.forceLoad(); CameraServerCvJNI.forceLoad(); } catch (IOException e) { - var errorStr = SettingsManager.getCurrentPlatform().equals(SettingsManager.Platform.UNSUPPORTED) ? "Unsupported platform!" : "Failed to load JNI Libraries!"; + var errorStr = Platform.getCurrentPlatform().equals(Platform.UNSUPPORTED) ? "Unsupported platform!" : "Failed to load JNI Libraries!"; throw new RuntimeException(errorStr); } 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..12acdd962 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/Platform.java @@ -0,0 +1,52 @@ +package com.chameleonvision.settings; + +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 boolean isWindows() { + return this == WINDOWS_64; + } + + public boolean isLinux() { + return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_AARCH64; + } + + public boolean isMac() { + return this == MACOS_64; + } + + 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; + } +} diff --git a/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java b/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java index 221b2974e..1fbaf09d9 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.settings.network.NetworkManager; +import com.chameleonvision.util.FileHelper; import com.chameleonvision.vision.camera.CameraManager; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -15,20 +15,21 @@ 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) { 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(); + NetworkManager.init(); +// 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) { @@ -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/settings/network/INetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/INetworking.java new file mode 100644 index 000000000..e34e736d3 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/INetworking.java @@ -0,0 +1,14 @@ +package com.chameleonvision.settings.network; + +import java.net.SocketException; +import java.util.List; + +public interface INetworking { + String getHostname(); + NetworkIPMode getIPMode(); + boolean setDHCP(); + boolean setHostname(String hostname); + boolean setStatic(String ipAddress, String netmask, String gateway); + List getNetworkInterfaces() throws SocketException; + +} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java new file mode 100644 index 000000000..9b664826b --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java @@ -0,0 +1,37 @@ +package com.chameleonvision.settings.network; + +import java.net.SocketException; +import java.util.List; + +public class LinuxNetworking implements INetworking { + + @Override + public String getHostname() { + return null; + } + + @Override + public NetworkIPMode getIPMode() { + return null; + } + + @Override + public boolean setDHCP() { + return false; + } + + @Override + public boolean setHostname(String hostname) { + return false; + } + + @Override + public boolean setStatic(String ipAddress, String netmask, String gateway) { + return false; + } + + @Override + public List getNetworkInterfaces() throws SocketException { + return null; + } +} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java b/Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java new file mode 100644 index 000000000..b3e28ee36 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java @@ -0,0 +1,7 @@ +package com.chameleonvision.settings.network; + +public enum NetworkIPMode { + DHCP, + STATIC, + UNKNOWN +} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java b/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java new file mode 100644 index 000000000..57e7adbbe --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java @@ -0,0 +1,23 @@ +package com.chameleonvision.settings.network; + +import com.chameleonvision.settings.NetworkSettings; +import com.chameleonvision.settings.SettingsManager; + +import java.net.InetAddress; +import java.util.Collections; + +public class NetworkInterface { + public final String name; + public final String displayName; +// public NetworkIPMode IPMode; +// public String IPAddress; +// public String Netmask; +// public String Gateway; + + public NetworkInterface(java.net.NetworkInterface inetface) { + + name = inetface.getName(); + displayName = inetface.getDisplayName(); + + } +} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java b/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java new file mode 100644 index 000000000..08bea3db9 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java @@ -0,0 +1,40 @@ +package com.chameleonvision.settings.network; + + +import com.chameleonvision.settings.Platform; + +import java.net.SocketException; +import java.util.ArrayList; +import java.util.List; + +public class NetworkManager { + private NetworkManager() {} + + private static INetworking networking; + + public static void init() { + Platform platform = Platform.getCurrentPlatform(); + + if (platform.isLinux()) { + networking = new LinuxNetworking(); + } else if (platform.isWindows()) { + networking = new WindowsNetworking(); + } + + List interfaces = new ArrayList<>(); + + try { + interfaces = networking.getNetworkInterfaces(); + } catch (SocketException e) { + e.printStackTrace(); + } + + if (interfaces != null) { + for (var inetface : interfaces) { + if (inetface.displayName.toLowerCase().contains("asus")) { +// networking.setHostname("BIGRIG"); + } + } + } + } +} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java new file mode 100644 index 000000000..9dfd9df2c --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java @@ -0,0 +1,83 @@ +package com.chameleonvision.settings.network; + +import com.chameleonvision.settings.NetworkSettings; +import com.chameleonvision.settings.SettingsManager; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +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 WindowsNetworking implements INetworking { + + @Override + public String getHostname() { + try { + InetAddress localhost = InetAddress.getLocalHost(); + return localhost.getHostName().split("/")[0]; + } catch (UnknownHostException e) { + return null; + } + } + + @Override + public NetworkIPMode getIPMode() { + return NetworkIPMode.UNKNOWN; + } + + @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) { + return false; + } + + @Override + public List getNetworkInterfaces() throws SocketException { + var netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces()); + + List goodInterfaces = new ArrayList<>(); + + var teamBytes = NetworkSettings.GetTeamNumberIPBytes(SettingsManager.GeneralSettings.team_number); + + for (var inetface : netInterfaces) { + if (inetface.getDisplayName().toLowerCase().contains("bluetooth")) continue; + if (inetface.getDisplayName().toLowerCase().contains("virtual")) continue; + if (inetface.getDisplayName().toLowerCase().contains("loopback")) continue; + if (!inetface.isUp()) continue; + for (var inetAddr : Collections.list(inetface.getInetAddresses())) { + var rawAddr = inetAddr.getAddress(); + if (rawAddr[1] == teamBytes[0] && rawAddr[2] == teamBytes[1]) { + goodInterfaces.add(new NetworkInterface(inetface)); + } + } + } + return goodInterfaces; + } +} 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/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/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 fd9fd3105..ca6f7420c 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; @@ -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; } 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 ebbb08bef..d8ba5e03d 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/VisionProcess.java b/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java index 96c95fcf0..858ce6f86 100644 --- a/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java +++ b/Main/src/main/java/com/chameleonvision/vision/process/VisionProcess.java @@ -110,14 +110,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; From 3db2f30ea710da4fb160cfc98ba6625e3635b775 Mon Sep 17 00:00:00 2001 From: Banks Troutman Date: Sun, 6 Oct 2019 17:26:17 -0400 Subject: [PATCH 2/4] Continued rework of networking, added root check --- Main/chameleon-vision.iml | 38 ++--- .../main/java/com/chameleonvision/Main.java | 41 ++++- .../network/LinuxNetworking.java | 99 ++++++++++++ .../{settings => }/network/NetworkIPMode.java | 2 +- .../network/NetworkInterface.java | 57 +++++++ .../network/NetworkManager.java | 122 +++++++++++++++ .../network/SysNetworking.java | 37 +++++ .../network/WindowsNetworking.java | 57 +++++++ .../chameleonvision/settings/Platform.java | 29 +++- .../settings/SettingsManager.java | 10 +- .../settings/network/INetworking.java | 14 -- .../settings/network/LinuxNetworking.java | 37 ----- .../settings/network/NetworkInterface.java | 23 --- .../settings/network/NetworkManager.java | 40 ----- .../settings/network/WindowsNetworking.java | 83 ----------- .../{Handler => util}/MathHandler.java | 9 +- .../com/chameleonvision/util/ShellExec.java | 141 ++++++++++++++++++ .../vision/process/CVProcess.java | 2 +- 18 files changed, 607 insertions(+), 234 deletions(-) create mode 100644 Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java rename Main/src/main/java/com/chameleonvision/{settings => }/network/NetworkIPMode.java (54%) create mode 100644 Main/src/main/java/com/chameleonvision/network/NetworkInterface.java create mode 100644 Main/src/main/java/com/chameleonvision/network/NetworkManager.java create mode 100644 Main/src/main/java/com/chameleonvision/network/SysNetworking.java create mode 100644 Main/src/main/java/com/chameleonvision/network/WindowsNetworking.java delete mode 100644 Main/src/main/java/com/chameleonvision/settings/network/INetworking.java delete mode 100644 Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java delete mode 100644 Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java delete mode 100644 Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java delete mode 100644 Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java rename Main/src/main/java/com/chameleonvision/{Handler => util}/MathHandler.java (84%) create mode 100644 Main/src/main/java/com/chameleonvision/util/ShellExec.java 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/src/main/java/com/chameleonvision/Main.java b/Main/src/main/java/com/chameleonvision/Main.java index e380eec0a..4b47c7db3 100644 --- a/Main/src/main/java/com/chameleonvision/Main.java +++ b/Main/src/main/java/com/chameleonvision/Main.java @@ -1,16 +1,18 @@ package com.chameleonvision; +import com.chameleonvision.network.NetworkManager; import com.chameleonvision.settings.Platform; import com.chameleonvision.settings.SettingsManager; -import com.chameleonvision.settings.network.NetworkManager; import com.chameleonvision.util.Utilities; import com.chameleonvision.vision.camera.CameraManager; 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 { @@ -18,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(); @@ -44,6 +63,7 @@ public class Main { break; case NT_SERVERMODE_KEY: case NETWORK_MANAGE_KEY: + case IGNORE_ROOT: // nothing } @@ -78,6 +98,8 @@ public class Main { case NETWORK_MANAGE_KEY: manageNetwork = false; break; + case IGNORE_ROOT: + ignoreRoot = true; } } } @@ -85,23 +107,36 @@ public class Main { 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 { CameraServerJNI.forceLoad(); CameraServerCvJNI.forceLoad(); } catch (IOException e) { - var errorStr = Platform.getCurrentPlatform().equals(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..e22722324 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java @@ -0,0 +1,99 @@ +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() { + var ifaceName = networkInterface.name; + var ethResetCmd = String.format("ifconfig %s 0.0.0.0 0.0.0.0", ifaceName); + var dhclientCmd = String.format("dhclient %s", ifaceName); + + + // ifconfig eth0 0.0.0.0 0.0.0.0 + try { + int retCode = shell.execute("ifconfig", null, true, ifaceName, "0.0.0.0", "0.0.0.0"); + while (!shell.isOutputCompleted() && !shell.isErrorCompleted()) {} + var out = shell.getOutput(); + var err = shell.getError(); + if (retCode != 0) return false; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + try { + int retCode = shell.execute("dhclient", null, true, ifaceName); + while (!shell.isOutputCompleted() && !shell.isErrorCompleted()) {} + var out = shell.getOutput(); + var err = shell.getError(); + if (retCode != 0) return false; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + return true; + } + + @Override + public boolean setHostname(String newHostname) { + var cmdString = String.format("hostnamectl set-hostname %s", newHostname); + + try { + var process = Runtime.getRuntime().exec(cmdString); + var returnCode = shell.execute("hostnamectl", null, true, "set-hostname", newHostname); + return returnCode == 0; + } catch(Exception e) { + e.printStackTrace(); + return false; + } + } + + @Override + public boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast) { + try { + int clearRetCode = shell.execute("ip addr flush dev", null, true, networkInterface.name); + int setIPRetCode = shell.execute(String.format("ip addr add %s/%s broadcast %s dev %s", ipAddress, netmask, broadcast, networkInterface.name), null, true); + int setGatewayRetCode = shell.execute(String.format("ip route replace default via %s dev %s", gateway, networkInterface.name), null, false); + 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/settings/network/NetworkIPMode.java b/Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java similarity index 54% rename from Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java rename to Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java index b3e28ee36..ec5a376d3 100644 --- a/Main/src/main/java/com/chameleonvision/settings/network/NetworkIPMode.java +++ b/Main/src/main/java/com/chameleonvision/network/NetworkIPMode.java @@ -1,4 +1,4 @@ -package com.chameleonvision.settings.network; +package com.chameleonvision.network; public enum NetworkIPMode { DHCP, 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..a287e98c1 --- /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/settings/Platform.java b/Main/src/main/java/com/chameleonvision/settings/Platform.java index 12acdd962..1709a6ebd 100644 --- a/Main/src/main/java/com/chameleonvision/settings/Platform.java +++ b/Main/src/main/java/com/chameleonvision/settings/Platform.java @@ -1,10 +1,14 @@ 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_AARCH64("Linux ARM 64bit"), + LINUX_AARCH64("Linux For Tegra"), MACOS_64("Mac OS x64"), UNSUPPORTED("Unsupported Platform"); @@ -26,6 +30,29 @@ public enum Platform { 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"); diff --git a/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java b/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java index 1fbaf09d9..6a2a3c310 100644 --- a/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java +++ b/Main/src/main/java/com/chameleonvision/settings/SettingsManager.java @@ -1,6 +1,6 @@ package com.chameleonvision.settings; -import com.chameleonvision.settings.network.NetworkManager; +import com.chameleonvision.network.NetworkManager; import com.chameleonvision.util.FileHelper; import com.chameleonvision.vision.camera.CameraManager; import com.google.gson.Gson; @@ -19,10 +19,10 @@ public class SettingsManager { private SettingsManager() {} - public static void initialize(boolean manageNetwork) { + public static void initialize() { initGeneralSettings(); - if (manageNetwork) { - NetworkManager.init(); +// if (manageNetwork) { + // NetworkSettings netSettings = new NetworkSettings(); // netSettings.hostname = GeneralSettings.hostname; // netSettings.gateway = GeneralSettings.gateway; @@ -30,7 +30,7 @@ public class SettingsManager { // 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(); diff --git a/Main/src/main/java/com/chameleonvision/settings/network/INetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/INetworking.java deleted file mode 100644 index e34e736d3..000000000 --- a/Main/src/main/java/com/chameleonvision/settings/network/INetworking.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.chameleonvision.settings.network; - -import java.net.SocketException; -import java.util.List; - -public interface INetworking { - String getHostname(); - NetworkIPMode getIPMode(); - boolean setDHCP(); - boolean setHostname(String hostname); - boolean setStatic(String ipAddress, String netmask, String gateway); - List getNetworkInterfaces() throws SocketException; - -} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java deleted file mode 100644 index 9b664826b..000000000 --- a/Main/src/main/java/com/chameleonvision/settings/network/LinuxNetworking.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.chameleonvision.settings.network; - -import java.net.SocketException; -import java.util.List; - -public class LinuxNetworking implements INetworking { - - @Override - public String getHostname() { - return null; - } - - @Override - public NetworkIPMode getIPMode() { - return null; - } - - @Override - public boolean setDHCP() { - return false; - } - - @Override - public boolean setHostname(String hostname) { - return false; - } - - @Override - public boolean setStatic(String ipAddress, String netmask, String gateway) { - return false; - } - - @Override - public List getNetworkInterfaces() throws SocketException { - return null; - } -} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java b/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java deleted file mode 100644 index 57e7adbbe..000000000 --- a/Main/src/main/java/com/chameleonvision/settings/network/NetworkInterface.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.chameleonvision.settings.network; - -import com.chameleonvision.settings.NetworkSettings; -import com.chameleonvision.settings.SettingsManager; - -import java.net.InetAddress; -import java.util.Collections; - -public class NetworkInterface { - public final String name; - public final String displayName; -// public NetworkIPMode IPMode; -// public String IPAddress; -// public String Netmask; -// public String Gateway; - - public NetworkInterface(java.net.NetworkInterface inetface) { - - name = inetface.getName(); - displayName = inetface.getDisplayName(); - - } -} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java b/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java deleted file mode 100644 index 08bea3db9..000000000 --- a/Main/src/main/java/com/chameleonvision/settings/network/NetworkManager.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.chameleonvision.settings.network; - - -import com.chameleonvision.settings.Platform; - -import java.net.SocketException; -import java.util.ArrayList; -import java.util.List; - -public class NetworkManager { - private NetworkManager() {} - - private static INetworking networking; - - public static void init() { - Platform platform = Platform.getCurrentPlatform(); - - if (platform.isLinux()) { - networking = new LinuxNetworking(); - } else if (platform.isWindows()) { - networking = new WindowsNetworking(); - } - - List interfaces = new ArrayList<>(); - - try { - interfaces = networking.getNetworkInterfaces(); - } catch (SocketException e) { - e.printStackTrace(); - } - - if (interfaces != null) { - for (var inetface : interfaces) { - if (inetface.displayName.toLowerCase().contains("asus")) { -// networking.setHostname("BIGRIG"); - } - } - } - } -} diff --git a/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java b/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java deleted file mode 100644 index 9dfd9df2c..000000000 --- a/Main/src/main/java/com/chameleonvision/settings/network/WindowsNetworking.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.chameleonvision.settings.network; - -import com.chameleonvision.settings.NetworkSettings; -import com.chameleonvision.settings.SettingsManager; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -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 WindowsNetworking implements INetworking { - - @Override - public String getHostname() { - try { - InetAddress localhost = InetAddress.getLocalHost(); - return localhost.getHostName().split("/")[0]; - } catch (UnknownHostException e) { - return null; - } - } - - @Override - public NetworkIPMode getIPMode() { - return NetworkIPMode.UNKNOWN; - } - - @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) { - return false; - } - - @Override - public List getNetworkInterfaces() throws SocketException { - var netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces()); - - List goodInterfaces = new ArrayList<>(); - - var teamBytes = NetworkSettings.GetTeamNumberIPBytes(SettingsManager.GeneralSettings.team_number); - - for (var inetface : netInterfaces) { - if (inetface.getDisplayName().toLowerCase().contains("bluetooth")) continue; - if (inetface.getDisplayName().toLowerCase().contains("virtual")) continue; - if (inetface.getDisplayName().toLowerCase().contains("loopback")) continue; - if (!inetface.isUp()) continue; - for (var inetAddr : Collections.list(inetface.getInetAddresses())) { - var rawAddr = inetAddr.getAddress(); - if (rawAddr[1] == teamBytes[0] && rawAddr[2] == teamBytes[1]) { - goodInterfaces.add(new NetworkInterface(inetface)); - } - } - } - return goodInterfaces; - } -} diff --git a/Main/src/main/java/com/chameleonvision/Handler/MathHandler.java b/Main/src/main/java/com/chameleonvision/util/MathHandler.java similarity index 84% rename from Main/src/main/java/com/chameleonvision/Handler/MathHandler.java rename to Main/src/main/java/com/chameleonvision/util/MathHandler.java index 40971fffc..651c84da9 100644 --- a/Main/src/main/java/com/chameleonvision/Handler/MathHandler.java +++ b/Main/src/main/java/com/chameleonvision/util/MathHandler.java @@ -1,15 +1,20 @@ -package com.chameleonvision.Handler; +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); } } 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..65a7c4f47 --- /dev/null +++ b/Main/src/main/java/com/chameleonvision/util/ShellExec.java @@ -0,0 +1,141 @@ +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. + * @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/vision/process/CVProcess.java b/Main/src/main/java/com/chameleonvision/vision/process/CVProcess.java index d701efbf5..80063f605 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.*; From 47307e1fbd8a16528470bae4af1f9f2e492b5e9d Mon Sep 17 00:00:00 2001 From: Banks Troutman Date: Sun, 6 Oct 2019 21:49:17 -0400 Subject: [PATCH 3/4] Networking bugfixes, added PixelFormat to resolution selection --- .../main/java/com/chameleonvision/Main.java | 1 + .../network/LinuxNetworking.java | 47 ++++++------------- .../network/NetworkManager.java | 2 +- .../chameleonvision/settings/Platform.java | 8 ++-- .../com/chameleonvision/util/ShellExec.java | 10 ++++ .../chameleonvision/vision/camera/Camera.java | 4 +- .../vision/camera/CameraManager.java | 2 +- 7 files changed, 35 insertions(+), 39 deletions(-) diff --git a/Main/src/main/java/com/chameleonvision/Main.java b/Main/src/main/java/com/chameleonvision/Main.java index 4b47c7db3..ead5e6834 100644 --- a/Main/src/main/java/com/chameleonvision/Main.java +++ b/Main/src/main/java/com/chameleonvision/Main.java @@ -120,6 +120,7 @@ public class Main { // Attempt to load the JNI Libraries try { + if (CurrentPlatform.equals(Platform.LINUX_ARM64)) CameraServerJNI.forceLoad(); CameraServerCvJNI.forceLoad(); } catch (IOException e) { diff --git a/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java b/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java index e22722324..3e1d811de 100644 --- a/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java +++ b/Main/src/main/java/com/chameleonvision/network/LinuxNetworking.java @@ -19,45 +19,23 @@ public class LinuxNetworking extends SysNetworking { @Override public boolean setDHCP() { - var ifaceName = networkInterface.name; - var ethResetCmd = String.format("ifconfig %s 0.0.0.0 0.0.0.0", ifaceName); - var dhclientCmd = String.format("dhclient %s", ifaceName); - - - // ifconfig eth0 0.0.0.0 0.0.0.0 + String[] clearArgs = { "addr", "flush", "dev", networkInterface.name }; try { - int retCode = shell.execute("ifconfig", null, true, ifaceName, "0.0.0.0", "0.0.0.0"); - while (!shell.isOutputCompleted() && !shell.isErrorCompleted()) {} - var out = shell.getOutput(); - var err = shell.getError(); - if (retCode != 0) return false; + 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; } - - try { - int retCode = shell.execute("dhclient", null, true, ifaceName); - while (!shell.isOutputCompleted() && !shell.isErrorCompleted()) {} - var out = shell.getOutput(); - var err = shell.getError(); - if (retCode != 0) return false; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - return true; } @Override public boolean setHostname(String newHostname) { - var cmdString = String.format("hostnamectl set-hostname %s", newHostname); - + String[] setHostnameArgs = { "set-hostname", newHostname }; try { - var process = Runtime.getRuntime().exec(cmdString); - var returnCode = shell.execute("hostnamectl", null, true, "set-hostname", newHostname); - return returnCode == 0; + var setHostnameRetCode = shell.execute("hostnamectl", setHostnameArgs); + return setHostnameRetCode == 0; } catch(Exception e) { e.printStackTrace(); return false; @@ -67,9 +45,14 @@ public class LinuxNetworking extends SysNetworking { @Override public boolean setStatic(String ipAddress, String netmask, String gateway, String broadcast) { try { - int clearRetCode = shell.execute("ip addr flush dev", null, true, networkInterface.name); - int setIPRetCode = shell.execute(String.format("ip addr add %s/%s broadcast %s dev %s", ipAddress, netmask, broadcast, networkInterface.name), null, true); - int setGatewayRetCode = shell.execute(String.format("ip route replace default via %s dev %s", gateway, networkInterface.name), null, false); + 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(); diff --git a/Main/src/main/java/com/chameleonvision/network/NetworkManager.java b/Main/src/main/java/com/chameleonvision/network/NetworkManager.java index a287e98c1..294d041e6 100644 --- a/Main/src/main/java/com/chameleonvision/network/NetworkManager.java +++ b/Main/src/main/java/com/chameleonvision/network/NetworkManager.java @@ -81,7 +81,7 @@ public class NetworkManager { } var genSettings = SettingsManager.GeneralSettings; - boolean isStatic = genSettings.connection_type.toLowerCase().equals("Static"); + boolean isStatic = genSettings.connection_type.toLowerCase().equals("static"); if (isStatic) { var splitIPAddr = genSettings.ip.split("\\."); diff --git a/Main/src/main/java/com/chameleonvision/settings/Platform.java b/Main/src/main/java/com/chameleonvision/settings/Platform.java index 1709a6ebd..b4a4daf55 100644 --- a/Main/src/main/java/com/chameleonvision/settings/Platform.java +++ b/Main/src/main/java/com/chameleonvision/settings/Platform.java @@ -8,7 +8,8 @@ public enum Platform { WINDOWS_64("Windows x64"), LINUX_64("Linux x64"), LINUX_RASPBIAN("Linux Raspbian"), - LINUX_AARCH64("Linux For Tegra"), + LINUX_TEGRA("Linux For Tegra"), + LINUX_ARM64("Linux ARM64"), MACOS_64("Mac OS x64"), UNSUPPORTED("Unsupported Platform"); @@ -23,7 +24,7 @@ public enum Platform { } public boolean isLinux() { - return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_AARCH64; + return this == LINUX_64 || this == LINUX_RASPBIAN || this == LINUX_ARM64 || this == LINUX_TEGRA; } public boolean isMac() { @@ -63,9 +64,10 @@ public enum Platform { } 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_64; + if (osArch.contains("aarch")) return Platform.LINUX_ARM64; return Platform.UNSUPPORTED; } diff --git a/Main/src/main/java/com/chameleonvision/util/ShellExec.java b/Main/src/main/java/com/chameleonvision/util/ShellExec.java index 65a7c4f47..a6347edba 100644 --- a/Main/src/main/java/com/chameleonvision/util/ShellExec.java +++ b/Main/src/main/java/com/chameleonvision/util/ShellExec.java @@ -19,6 +19,16 @@ public class ShellExec { 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") 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 ca6f7420c..23038d26d 100644 --- a/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java +++ b/Main/src/main/java/com/chameleonvision/vision/camera/Camera.java @@ -16,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; @@ -78,7 +78,7 @@ public class Camera { System.out.printf("Camera initialized in %.2fms\n", initTimeMs); } var trueVideoModes = UsbCam.enumerateVideoModes(); - availableVideoModes = Arrays.stream(trueVideoModes).filter(v -> v.fps >= MINIMUM_FPS && v.width >= MINIMUM_WIDTH && v.height >= MINIMUM_HEIGHT && v.pixelFormat == VideoMode.PixelFormat.kYUYV).toArray(VideoMode[]::new); + 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/vision/camera/CameraManager.java b/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java index d8ba5e03d..a7baa51a8 100644 --- a/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java +++ b/Main/src/main/java/com/chameleonvision/vision/camera/CameraManager.java @@ -118,7 +118,7 @@ public class CameraManager { public static List getResolutionList() throws CameraException { if (!SettingsManager.GeneralSettings.curr_camera.equals("")) { return Arrays.stream(CameraManager.getCamera(SettingsManager.GeneralSettings.curr_camera).getAvailableVideoModes()) - .map(res -> String.format("%s X %s at %s fps", res.width, res.height, res.fps)).collect(Collectors.toList()); + .map(res -> String.format("%s X %s at %s fps, mode: %s", res.width, res.height, res.fps, res.pixelFormat.name())).collect(Collectors.toList()); } throw new CameraException(CameraException.CameraExceptionType.NO_CAMERA); } From 53585be0887886149706217782638783fe262108 Mon Sep 17 00:00:00 2001 From: Banks Troutman Date: Mon, 7 Oct 2019 14:04:20 -0400 Subject: [PATCH 4/4] Update WPILib dependencies --- Main/pom.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Main/pom.xml b/Main/pom.xml index 0449c7f57..0d9b904d1 100644 --- a/Main/pom.xml +++ b/Main/pom.xml @@ -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-200-g2271570 + 2019.4.1-213-g56d782b edu.wpi.first.ntcore ntcore-java - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b edu.wpi.first.ntcore ntcore-jni - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b osxx86-64 edu.wpi.first.ntcore ntcore-jni - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b linuxraspbian edu.wpi.first.ntcore ntcore-jni - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b linuxx86-64 edu.wpi.first.ntcore ntcore-jni - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b windowsx86-64 @@ -194,7 +194,7 @@ edu.wpi.first.wpiutil wpiutil-java - 2019.4.1-200-g2271570 + 2019.4.1-213-g56d782b