Update backend to provide more useful info to frontend (#866)

This commit is contained in:
Sriman Achanta
2023-06-25 21:07:27 -04:00
committed by GitHub
parent 7593c5ed05
commit 715ef62c85
17 changed files with 858 additions and 505 deletions

View File

@@ -345,7 +345,7 @@ export default {
this.previouslySelectedIndices = null;
},
switchToSettingsTab() {
this.axios.post('http://' + this.$address + '/api/sendMetrics', {})
this.axios.post('http://' + this.$address + '/api/utils/publishMetrics')
}
}
};

View File

@@ -1,5 +1,13 @@
<template>
<div>
<v-snackbar
v-model="snack"
top
:color="snackbar.color"
:timeout="2000"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
<v-row
align="center"
style="padding: 12px 12px 12px 24px"
@@ -269,7 +277,12 @@ export default {
duplicateDialog: false,
showPipeTypeDialog: false,
proposedPipelineType : 0,
pipeIndexToDuplicate: undefined
pipeIndexToDuplicate: undefined,
snack: false,
snackbar: {
color: "success",
text: "",
}
}
},
computed: {
@@ -347,16 +360,37 @@ export default {
},
saveCameraNameChange() {
if (this.checkCameraName === "") {
// this.handleInputWithIndex("changeCameraName", this.newCameraName);
this.axios.post('http://' + this.$address + '/api/setCameraNickname',
this.axios.post('http://' + this.$address + '/api/settings/camera/setNickname',
{name: this.newCameraName, cameraIndex: this.$store.getters.currentCameraIndex})
// eslint-disable-next-line
.then(r => {
this.$emit('camera-name-changed')
.then(response => {
this.$emit('camera-name-changed')
this.snackbar = {
color: "success",
text: response.data.text || response.data
}
this.snack = true;
})
.catch(e => {
console.log("HTTP error while changing camera name " + e);
this.$emit('camera-name-changed')
.catch(error => {
this.$emit('camera-name-changed')
if(error.response) {
this.snackbar = {
color: "error",
text: error.response.data.text || error.response.data
}
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
this.discardCameraNameChange();
}
@@ -404,7 +438,3 @@ export default {
}
</script>
<style scoped>
</style>

View File

@@ -302,6 +302,7 @@
<v-col>
<v-btn
color="secondary"
:disabled="isCalibrating"
small
style="width: 100%;"
@click="$refs.importCalibrationFromCalibdb.click()"
@@ -330,7 +331,7 @@
style="border-radius: 5px;"
/>
<v-dialog
v-model="snack"
v-model="calibrationDialog"
width="500px"
:persistent="true"
>
@@ -399,12 +400,12 @@
>
<v-snackbar
v-model="uploadSnack"
v-model="snack"
top
:color="uploadSnackData.color"
timeout="-1"
:color="snackbar.color"
timeout="2000"
>
<span>{{ uploadSnackData.text }}</span>
<span>{{ snackbar.text }}</span>
</v-snackbar>
</div>
</template>
@@ -431,17 +432,17 @@ export default {
},
data() {
return {
snack: false,
calibrationDialog: false,
calibrationInProgress: false,
calibrationFailed: false,
filteredVideomodeIndex: 0,
settingsValid: true,
unfilteredStreamDivisors: [1, 2, 4],
uploadSnackData: {
snackbar: {
color: "success",
text: "",
},
uploadSnack: false,
snack: false,
}
},
computed: {
@@ -617,43 +618,34 @@ export default {
};
this.axios
.post("http://" + this.$address + "/api/calibration/import", data, {
.post("http://" + this.$address + "/api/calibration/importFromCalibDB", data, {
headers: { "Content-Type": "text/plain" },
})
.then(() => {
this.uploadSnackData = {
color: "success",
text:
"Calibration imported successfully!",
};
this.uploadSnack = true;
.then((response) => {
this.snackbar = {
color: response.status === 200 ? "success" : "error",
text: response.data.text || response.data
}
this.snack = true;
})
.catch((err) => {
if (err.response) {
this.uploadSnackData = {
if (err.request) {
this.snackbar = {
color: "error",
text:
"Error while uploading calibration file! Could not process provided file.",
};
} else if (err.request) {
this.uploadSnackData = {
color: "error",
text:
"Error while uploading calibration file! No respond to upload attempt.",
text: "Error while uploading calibration file! The backend didn't respond to the upload attempt.",
};
} else {
this.uploadSnackData = {
this.snackbar = {
color: "error",
text: "Error while uploading calibration file!",
};
}
this.uploadSnack = true;
this.snack = true;
});
})
},
closeDialog() {
this.snack = false;
this.calibrationDialog = false;
this.calibrationInProgress = false;
this.calibrationFailed = false;
},
@@ -747,15 +739,33 @@ export default {
doc.save(`calibrationTarget-${config.type}.pdf`)
},
sendCameraSettings() {
this.axios.post("http://" + this.$address + "/api/settings/camera", {
"settings": this.cameraSettings,
"index": this.$store.state.currentCameraIndex
}).then(response => {
if (response.status === 200) {
this.$store.state.saveBar = true;
this.axios.post("http://" + this.$address + "/api/settings/camera", {"settings": this.cameraSettings, "index": this.$store.state.currentCameraIndex})
.then(response => {
this.snackbar = {
color: "success",
text: response.data.text || response.data
}
this.snack = true;
})
.catch(error => {
if(error.response) {
this.snackbar = {
color: "error",
text: error.response.data.text || error.response.data
}
}
)
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
},
isCalibrated(resolution) {
return this.$store.getters.currentCameraSettings.calibrations
@@ -782,16 +792,13 @@ export default {
sendCalibrationFinish() {
console.log("finishing calibration for index " + this.$store.getters.currentCameraIndex);
this.snack = true;
this.calibrationDialog = true;
this.calibrationInProgress = true;
this.axios.post("http://" + this.$address + "/api/settings/endCalibration", {idx: this.$store.getters.currentCameraIndex})
.then((response) => {
if (response.status === 200) {
this.calibrationInProgress = false;
} else {
this.calibrationFailed = true;
}
this.axios.post("http://" + this.$address + "/api/calibration/end", {index: this.$store.getters.currentCameraIndex})
.then(() => {
// End calibration will always return a 200 code on success
this.calibrationInProgress = false;
}
).catch(() => {
this.calibrationFailed = true;

View File

@@ -23,7 +23,7 @@
<a
ref="exportLogFile"
style="color: black; text-decoration: none; display: none"
:href="'http://' + this.$address + '/api/settings/photonvision-journalctl.txt'"
:href="'http://' + this.$address + '/api/utils/logs/photonvision-journalctl.txt'"
download="photonvision-journalctl.txt"
/>
</v-btn>

View File

@@ -43,7 +43,7 @@
/>
<v-snackbar
v-model="snackbar"
:timeout="3000"
:timeout="2000"
top
color="error"
>

View File

@@ -54,7 +54,7 @@
>
<v-btn
color="secondary"
@click="$refs.importSettings.click()"
@click="() => showImportDialog = true"
>
<v-icon left>
mdi-import
@@ -62,7 +62,6 @@
Import Settings
</v-btn>
</v-col>
<v-col
cols="12"
sm="6"
@@ -77,7 +76,6 @@
Export Settings
</v-btn>
</v-col>
<v-col
cols="12"
sm="6"
@@ -95,16 +93,11 @@
<a
ref="exportLogFile"
style="color: black; text-decoration: none; display: none"
:href="
'http://' +
this.$address +
'/api/settings/photonvision-journalctl.txt'
"
:href="'http://' + this.$address + '/api/utils/logs/photonvision-journalctl.txt'"
download="photonvision-journalctl.txt"
/>
</v-btn>
</v-col>
<v-col
cols="12"
sm="6"
@@ -124,24 +117,71 @@
v-model="snack"
top
:color="snackbar.color"
timeout="-1"
:timeout="snackbarTimeout"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
<!-- Special hidden upload input that gets 'clicked' when the user imports settings -->
<input
ref="importSettings"
type="file"
accept=".zip, .json"
style="display: none;"
@change="readImportedSettings"
<v-dialog
v-model="showImportDialog"
width="600"
@input="() => {
importType = undefined;
importFile = null;
}"
>
<v-card
color="primary"
dark
>
<v-card-title>Import Settings</v-card-title>
<v-card-text>
Upload and apply previously saved or exported PhotonVision settings to this device
<v-row
class="mt-6 ml-8"
>
<CVselect
v-model="importType"
name="Type"
tooltip="Select the type of settings file you are trying to upload"
:list="['All Settings', 'Hardware Config', 'Hardware Settings', 'Network Config']"
:select-cols="10"
/>
</v-row>
<v-row
class="mt-6 ml-8 mr-8"
>
<v-file-input
:disabled="importType === undefined"
:error-messages="importType === undefined ? 'Settings type not selected' : ''"
:accept="importType === 0 ? '.zip' : '.json'"
@change="(file) => importFile = file"
/>
</v-row>
<v-row
class="mt-12 ml-8 mr-8 mb-1"
style="display: flex; align-items: center; justify-content: center"
align="center"
>
<v-btn
color="secondary"
:disabled="importFile === null"
@click="uploadSettings"
>
<v-icon left>
mdi-import
</v-icon>
Import Settings
</v-btn>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
<!-- Special hidden link that gets 'clicked' when the user exports settings -->
<a
ref="exportSettings"
style="color: black; text-decoration: none; display: none"
:href="'http://' + this.$address + '/api/settings/photonvision_config.zip'"
:href="`http://${this.$address}/api/settings/photonvision_config.zip`"
download="photonvision-settings.zip"
/>
@@ -156,18 +196,28 @@
</div>
</template>
<script>
import CVselect from "../../components/common/cv-select";
export default {
// eslint-disable-next-line
name: "DeviceControl",
components: {
CVselect
},
data() {
return {
snack: false,
snackbarTimeout: 2000,
uploadPercentage: 0.0,
showImportDialog: false,
importType: undefined,
importFile: null,
snackbar: {
color: "success",
text: "",
},
}
};
},
computed: {
@@ -197,51 +247,117 @@ export default {
metrics() {
// console.log(this.$store.state.metrics);
return this.$store.state.metrics;
},
}
},
methods: {
restartProgram() {
this.axios.post("http://" + this.$address + "/api/restartProgram", {});
this.axios.post("http://" + this.$address + "/api/utils/restartProgram")
.then(() => {
this.snackbar = {
color: "success",
text: "Successfully sent program restart request"
}
this.snack = true;
})
.catch(error => {
// This endpoint always return 204 regardless of outcome
if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
},
restartDevice() {
this.axios.post("http://" + this.$address + "/api/restartDevice", {});
this.axios.post("http://" + this.$address + "/api/utils/restartDevice")
.then(() => {
this.snackbar = {
color: "success",
text: "Successfully dispatched the restart command. It isn't confirmed if a device restart will occur."
}
this.snack = true;
})
.catch(error => {
if(error.response) {
this.snackbar = {
color: "error",
text: "The backend is unable to fulfil the request to restart the device."
}
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
},
readImportedSettings(event) {
uploadSettings() {
let formData = new FormData();
formData.append("zipData", event.target.files[0]);
this.axios
.post("http://" + this.$address + "/api/settings/import", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then(() => {
this.snackbar = {
color: "success",
text:
"Settings imported successfully! PhotonVision will restart in the background...",
};
this.snack = true;
})
.catch((err) => {
if (err.response) {
formData.append("data", this.importFile);
let settingsType
switch (this.importType) {
case 0:
settingsType = ""
break;
case 1:
settingsType = "/hardwareConfig"
break;
case 2:
settingsType = "/hardwareSettings"
break;
case 3:
settingsType = "/networkConfig"
break;
}
const requestUrl = `http://${this.$address}/api/settings${settingsType}`;
this.axios.post(requestUrl, formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then(response => {
this.snackbar = {
color: "error",
text:
"Error while uploading settings file! Could not process provided file.",
};
} else if (err.request) {
this.snackbar = {
color: "error",
text:
"Error while uploading settings file! No respond to upload attempt.",
};
} else {
this.snackbar = {
color: "error",
text: "Error while uploading settings file!",
};
}
this.snack = true;
});
color: "success",
text: response.data.text || response.data
}
this.snack = true;
})
.catch(error => {
if(error.response) {
this.snackbar = {
color: "error",
text: error.response.data.text || error.response.data
}
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
this.showImportDialog = false
this.importType = undefined;
this.importFile = null;
},
doOfflineUpdate(event) {
this.snackbar = {
@@ -249,12 +365,13 @@ export default {
text: "New Software Upload in Process...",
};
this.snack = true;
this.snackbarTimeout = -1
let formData = new FormData();
formData.append("jarData", event.target.files[0]);
this.axios
.post(
"http://" + this.$address + "/api/settings/offlineUpdate",
"http://" + this.$address + "/api/utils/offlineUpdate",
formData,
{
headers: { "Content-Type": "multipart/form-data" },
@@ -273,38 +390,37 @@ export default {
}.bind(this),
}
)
.then(() => {
this.snackbar = {
color: "success",
text:
"New .jar copied successfully! PhotonVision will restart in the background...",
};
this.snack = true;
})
.catch((err) => {
if (err.response) {
.then(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;
});
color: "success",
text: response.data.text || response.data
}
this.snack = true;
})
.catch(error => {
if(error.response) {
this.snackbar = {
color: "error",
text: error.response.data.text || error.response.data
};
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
})
// Reset the timeout after the loading bar
this.snackbarTimeout = 2000
},
showLogs(event) {
event;
showLogs() {
this.$store.state.logsOverlay = true;
},
},

View File

@@ -75,7 +75,7 @@
v-model="snack"
top
:color="snackbar.color"
timeout="5000"
timeout="2000"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
@@ -230,31 +230,40 @@ export default {
};
this.snack = true;
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings).then(
response => {
if (response.status === 200) {
this.snackbar = {
color: "success",
text: "Settings updated successfully"
};
this.snack = true;
}
},
error => {
if (error.status === 504 || changingStaticIp) {
this.snackbar = {
this.axios.post("http://" + this.$address + "/api/settings/general", this.settings)
.then(response => {
this.snackbar = {
color: "success",
text: response.data.text || response.data
}
this.snack = true;
})
.catch(error => {
if(error.response) {
if (error.status === 504 || changingStaticIp) {
this.snackbar = {
color: "error",
text: (error.response || {data: `Connection lost! Try the new static IP at ${this.staticIp}:5800 or ${this.hostname}:5800 ?`}).data
text: `Connection lost! Try the new static IP at ${this.staticIp}:5800 or ${this.hostname}:5800?`
};
} else {
this.snackbar = {
color: "error",
text: error.response.data.text || error.response.data
}
}
} else if(error.request) {
this.snackbar = {
color: "error",
text: "Error while trying to process the request! The backend didn't respond.",
};
} else {
this.snackbar = {
color: "error",
text: (error.response || {data: "Couldn't save settings"}).data
color: "error",
text: "An error occurred while trying to process the request.",
};
}
this.snack = true;
}
)
this.snack = true;
})
},
},

View File

@@ -117,31 +117,12 @@
</tr>
</table>
</v-row>
<v-snackbar
v-model="snack"
top
:color="snackbar.color"
timeout="-1"
>
<span>{{ snackbar.text }}</span>
</v-snackbar>
</div>
</template>
<script>
export default {
name: 'Stats',
data() {
return {
snack: false,
uploadPercentage: 0.0,
snackbar: {
color: "success",
text: ""
},
}
},
computed: {
settings() {
return this.$store.state.settings.general;