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
+]