diff --git a/docs/source/docs/quick-start/common-setups.md b/docs/source/docs/quick-start/common-setups.md index 7d6ffaf602..dfd37e9f50 100644 --- a/docs/source/docs/quick-start/common-setups.md +++ b/docs/source/docs/quick-start/common-setups.md @@ -13,6 +13,11 @@ PhotonVision requires dedicated hardware, above and beyond a roboRIO. This page The Orange Pi 5 is the only currently supported device for object detection. ::: +## SystemCore Support + +The SystemCore is not supported by PhotonVision. PhotonVision is designed to utilize the entirety of the coprocessor's resources, and this could prove to be dangerous if attempted on the main robot controller. +There are no current plans to support running on SystemCore alongside robot code, and any attempts to do so are entirely at your own risk and will require a separate fork of PhotonVision. + ## SD Cards - 8GB or larger micro SD card diff --git a/photon-server/src/main/java/org/photonvision/Main.java b/photon-server/src/main/java/org/photonvision/Main.java index b07538a587..8cd5a344e1 100644 --- a/photon-server/src/main/java/org/photonvision/Main.java +++ b/photon-server/src/main/java/org/photonvision/Main.java @@ -18,9 +18,14 @@ package org.photonvision; import edu.wpi.first.hal.HAL; +import java.io.File; import edu.wpi.first.math.geometry.Rotation2d; import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Path; +import java.security.CodeSource; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.List; import org.apache.commons.cli.*; @@ -212,6 +217,70 @@ private static void addTestModeSources() { VisionSourceManager.getInstance().registerLoadedConfigs(cameraConfigs); } + private static void serveDenialPage() { + String docsLink = + "https://docs.photonvision.org/en/latest/docs/quick-start/common-setups.html#systemcore-support"; + + logger.error( + "SystemCore is not a supported platform for PhotonVision!\n " + + "Please visit " + + docsLink + + " for more information."); + + try { + int port = 5800; + io.javalin.Javalin app = null; + try { + app = io.javalin.Javalin.create(cfg -> cfg.showJavalinBanner = false).start(port); + } catch (Exception e) { + logger.warn("Failed to bind to port 5800, exiting: " + e.getMessage()); + port = DEFAULT_WEBPORT; + app = io.javalin.Javalin.create(cfg -> cfg.showJavalinBanner = false).start(port); + } + + final int boundPort = port; + final String html = + "" + + "
Main Robot Controllers shouldn't run PhotonVision, but yours does! " + + "Please uninstall PhotonVision. " + + "If you choose to modify PhotonVision so that it functions on SystemCore, " + + "you do so entirely at your own risk and without any support. " + + "For more information, see " + + docsLink + + ".
"; + + app.get( + "/", + ctx -> { + ctx.contentType("text/html; charset=utf-8"); + ctx.result(html); + }); + + logger.info( + "Served SystemCore warning page on port " + + boundPort + + " - process will remain running to serve the page."); + + // Prevent main from exiting so the page remains available. + final Object lock = new Object(); + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException ignored) { + } + } + } catch (Exception e) { + logger.error("Failed to start static warning page server", e); + } + + // Exit + System.exit(1); + } + private static void tryLoadJNI(JNITypes type) { try { LoadJNI.forceLoad(type); @@ -241,6 +310,10 @@ public static void main(String[] args) { + Platform.getPlatformName() + (Platform.isRaspberryPi() ? (" (Pi " + PiVersion.getPiVersion() + ")") : "")); + if (Platform.isSystemCore()) { + serveDenialPage(); + } + if (OsImageData.IMAGE_METADATA.isPresent()) { logger.info("PhotonVision image data: " + OsImageData.IMAGE_METADATA.get()); } else if (OsImageData.IMAGE_VERSION.isPresent()) { @@ -360,4 +433,21 @@ public static void main(String[] args) { HardwareManager.getInstance().setError(null); Server.initialize(DEFAULT_WEBPORT); } + + public static File getJarLocation() { + try { + ProtectionDomain protectionDomain = Main.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + if (codeSource != null) { + URL location = codeSource.getLocation(); + return new File(location.toURI()); + } else { + logger.error("Could not determine JAR location: code source is null"); + return null; + } + } catch (URISyntaxException e) { + logger.error("Error determining JAR location", e); + return null; + } + } } diff --git a/photon-targeting/src/main/java/org/photonvision/common/hardware/Platform.java b/photon-targeting/src/main/java/org/photonvision/common/hardware/Platform.java index 65c1970ba9..55ce4edafd 100644 --- a/photon-targeting/src/main/java/org/photonvision/common/hardware/Platform.java +++ b/photon-targeting/src/main/java/org/photonvision/common/hardware/Platform.java @@ -42,6 +42,12 @@ public enum Platform { true, OSType.LINUX, true), // Raspberry Pi 3/4 with a 64-bit image + LINUX_SYSTEMCORE( + "Linux Systemcore 64-bit NOT SUPPORTED", + Platform::getUnknownModel, + false, + OSType.LINUX, + false), // SystemCore 64-bit LINUX_RK3588_64( "Linux AARCH 64-bit with RK3588", Platform::getLinuxDeviceTreeModel, @@ -123,6 +129,11 @@ public static boolean isRK3588() { || fileHasText("/proc/device-tree/compatible", "rk3588"); } + public static boolean isSystemCore() { + File sysCore = new File("/home/systemcore"); + return sysCore.exists() | fileHasText("/etc/os-release", "systemcore"); + } + public static boolean isQCS6490() { return currentPlatform == LINUX_QCS6490 || Platform.isRubik(); } @@ -202,7 +213,9 @@ public static Platform getCurrentPlatform() { } if (OS_NAME.startsWith("Linux")) { - if (isPiSBC()) { + if (isSystemCore()) { + return LINUX_SYSTEMCORE; + } else if (isPiSBC()) { if (OS_ARCH.equals("arm") || OS_ARCH.equals("arm32")) { return LINUX_RASPBIAN32; } else if (OS_ARCH.equals("aarch64") || OS_ARCH.equals("arm64")) {