diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9351f661b..4a39276ae 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -160,7 +160,7 @@ jobs: - run: | NEW_JAR=$(realpath $(find . -name photonvision\*.jar)) sudo apt install unzip zip - curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi - + curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/tags/v2021.1.4 | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi - FILE_NAME=$(ls | grep image_*.zip) unzip $FILE_NAME IMAGE_FILE=$(ls | grep *.img) @@ -325,7 +325,7 @@ jobs: run: | NEW_JAR=$(realpath $(find . -name photonvision\*.jar)) sudo apt install unzip zip - curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi - + curl -sk https://api.github.com/repos/photonvision/photon-pi-gen/releases/tags/v2021.1.4 | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d '"' | wget -qi - FILE_NAME=$(ls | grep image_*.zip) unzip $FILE_NAME IMAGE_FILE=$(ls | grep *.img) diff --git a/photon-client/src/views/CamerasView.vue b/photon-client/src/views/CamerasView.vue index d09637022..f00cc5dac 100644 --- a/photon-client/src/views/CamerasView.vue +++ b/photon-client/src/views/CamerasView.vue @@ -19,15 +19,16 @@

diff --git a/photon-client/src/views/SettingsView.vue b/photon-client/src/views/SettingsView.vue index 0c8731030..c7842d37a 100644 --- a/photon-client/src/views/SettingsView.vue +++ b/photon-client/src/views/SettingsView.vue @@ -49,6 +49,7 @@ return { selectedTab: 0, snack: false, + calibrationInProgress: false, snackbar: { color: "accent", text: "" diff --git a/photon-client/src/views/SettingsViews/General.vue b/photon-client/src/views/SettingsViews/General.vue index 4dcc757e9..2214a8e9e 100644 --- a/photon-client/src/views/SettingsViews/General.vue +++ b/photon-client/src/views/SettingsViews/General.vue @@ -91,7 +91,7 @@ + + + mdi-update + + Offline Update + + + + + + @@ -181,6 +204,7 @@ export default { data() { return { snack: false, + uploadPercentage: 0.0, snackbar: { color: "success", text: "" @@ -249,6 +273,52 @@ export default { this.snack = true; }); }, + doOfflineUpdate(event) { + this.snackbar = { + color: "secondary", + text: "New Software Upload in Process..." + }; + this.snack = true; + + let formData = new FormData(); + formData.append("jarData", event.target.files[0]); + this.axios.post("http://" + this.$address + "/api/settings/offlineUpdate", formData, + {headers: {"Content-Type": "multipart/form-data"}, + onUploadProgress: function( progressEvent ) { + this.uploadPercentage = parseInt( Math.round( ( progressEvent.loaded / progressEvent.total ) * 100 ) ); + if(this.uploadPercentage < 99.5){ + this.snackbar.text = "New Software Upload in Process, " + this.uploadPercentage + "% complete"; + } else { + this.snackbar.text = "Installing uploaded software..."; + } + + }.bind(this) + }).then(() => { + this.snackbar = { + color: "success", + text: "New .jar copied successfully! Program will now exit...", + }; + this.snack = true; + }).catch(err => { + if (err.response) { + this.snackbar = { + color: "error", + text: "Error while uploading new .jar file! Could not process provided file.", + }; + } else if (err.request) { + this.snackbar = { + color: "error", + text: "Error while uploading new .jar file! No respond to upload attempt.", + }; + } else { + this.snackbar = { + color: "error", + text: "Error while uploading new .jar file!", + }; + } + this.snack = true; + }); + }, } } @@ -266,6 +336,8 @@ export default { text-align: left; margin-bottom: 10px; width: 100%; + display: block; + overflow-x: auto; } .infoElem { diff --git a/photon-client/src/views/SettingsViews/Networking.vue b/photon-client/src/views/SettingsViews/Networking.vue index ed1005792..c5245cffc 100644 --- a/photon-client/src/views/SettingsViews/Networking.vue +++ b/photon-client/src/views/SettingsViews/Networking.vue @@ -10,9 +10,10 @@ name="Team Number" :rules="[v => (v > 0) || 'Team number must be greater than zero', v => (v < 10000) || 'Team number must have fewer than five digits']" class="mb-4" + :label-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7" /> - - + + Team number not set! NetworkTables cannot connect. @@ -42,6 +43,7 @@ name="Run NetworkTables Server (Debugging Only!)" tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot." class="mt-3 mb-3" + :text-cols="$vuetify.breakpoint.mdAndUp ? undefined : 7" /> @@ -60,7 +62,7 @@ - + - + . + */ +package org.photonvision.common.util.file; + +import java.io.File; +import java.net.URISyntaxException; + +public class ProgramDirectoryUtilities { + private static String getJarName() { + return new File( + ProgramDirectoryUtilities.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .getPath()) + .getName(); + } + + private static boolean runningFromJAR() { + String jarName = getJarName(); + return jarName.contains(".jar"); + } + + public static String getProgramDirectory() { + if (runningFromJAR()) { + return getCurrentJARDirectory(); + } else { + return System.getProperty("user.dir"); + } + } + + private static String getCurrentJARDirectory() { + try { + return new File( + ProgramDirectoryUtilities.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI() + .getPath()) + .getParent(); + } catch (URISyntaxException exception) { + exception.printStackTrace(); + } + + return null; + } +} diff --git a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java index fa58bb88e..53d6139e7 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -22,8 +22,11 @@ import edu.wpi.first.math.geometry.Rotation2d; import io.javalin.http.Context; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; @@ -37,6 +40,7 @@ import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.networking.NetworkManager; import org.photonvision.common.util.ShellExec; +import org.photonvision.common.util.file.ProgramDirectoryUtilities; import org.photonvision.vision.processes.VisionModuleManager; import org.photonvision.vision.target.TargetModel; @@ -103,6 +107,49 @@ public class RequestHandler { } } + public static void onOfflineUpdate(Context ctx) { + logger.info("Handling offline update .jar upload..."); + var file = ctx.uploadedFile("jarData"); + logger.info("New .jar uploaded successfully."); + + if (file != null) { + if (Platform.isRaspberryPi()) { + try { + Path filePath = + Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "photonvision.jar"); + File targetFile = new File(filePath.toString()); + var stream = new FileOutputStream(targetFile); + + logger.info( + "Streaming user-provided " + file.getFilename() + " into " + targetFile.toString()); + + file.getContent().transferTo(stream); + stream.close(); + + ctx.status(200); + logger.info("New .jar in place, going down for restart..."); + restartProgram(ctx); + + } catch (FileNotFoundException e) { + logger.error( + ".jar of this program could not be found. How the heck this program started in the first place is a mystery."); + ctx.status(500); + } catch (IOException e) { + logger.error("Could not overwrite the .jar for this instance of photonvision."); + ctx.status(500); + } + + } else { + logger.error("Hot .jar replace currently only supported on Raspberry pi. Ignoring."); + ctx.status(500); + } + + } else { + logger.error("Couldn't read provided file for new .jar! Ignoring."); + ctx.status(500); + } + } + @SuppressWarnings("unchecked") public static void onGeneralSettings(Context context) throws JsonProcessingException { Map map = diff --git a/photon-server/src/main/java/org/photonvision/server/Server.java b/photon-server/src/main/java/org/photonvision/server/Server.java index c2a321e15..87e8ed522 100644 --- a/photon-server/src/main/java/org/photonvision/server/Server.java +++ b/photon-server/src/main/java/org/photonvision/server/Server.java @@ -71,6 +71,7 @@ public class Server { }); /*API Events*/ app.post("/api/settings/import", RequestHandler::onSettingUpload); + app.post("/api/settings/offlineUpdate", RequestHandler::onOfflineUpdate); app.get("/api/settings/photonvision_config.zip", RequestHandler::onSettingsDownload); app.post("/api/settings/camera", RequestHandler::onCameraSettingsSave); app.post("/api/settings/general", RequestHandler::onGeneralSettings); diff --git a/photon-server/src/main/resources/web/index.html b/photon-server/src/main/resources/web/index.html index f008dfd12..b20a7cfba 100644 --- a/photon-server/src/main/resources/web/index.html +++ b/photon-server/src/main/resources/web/index.html @@ -1 +1 @@ -

UI has not been copied!

\ No newline at end of file +

UI has not been copied!

\ No newline at end of file diff --git a/photonlib-java-examples/bin/main/org/photonlib/examples/examples.json b/photonlib-java-examples/bin/main/org/photonlib/examples/examples.json index d007d0a42..40c05cc5a 100644 --- a/photonlib-java-examples/bin/main/org/photonlib/examples/examples.json +++ b/photonlib-java-examples/bin/main/org/photonlib/examples/examples.json @@ -59,4 +59,4 @@ "dependencies": [], "foldername": "simposeest" } -] \ No newline at end of file +]