mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-01 02:41:42 +00:00
Big scary buttons (#1471)
This commit is contained in:
@@ -76,6 +76,24 @@ if (!is_demo) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom scrollbar styles */
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #232c37;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #ffd843;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #e4c33c;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
background-color: #232c37;
|
||||
padding: 0 !important;
|
||||
|
||||
@@ -79,10 +79,10 @@ onBeforeUnmount(() => {
|
||||
<div class="stream-container" :style="containerStyle">
|
||||
<img :src="loadingImage" class="stream-loading" />
|
||||
<img
|
||||
:id="id"
|
||||
class="stream-video"
|
||||
ref="mjpgStream"
|
||||
v-show="streamSrc !== emptyStreamSrc"
|
||||
:id="id"
|
||||
ref="mjpgStream"
|
||||
class="stream-video"
|
||||
crossorigin="anonymous"
|
||||
:src="streamSrc"
|
||||
:alt="streamDesc"
|
||||
|
||||
@@ -3,8 +3,9 @@ import PvSelect from "@/components/common/pv-select.vue";
|
||||
import PvNumberInput from "@/components/common/pv-number-input.vue";
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
import { computed, ref, watchEffect } from "vue";
|
||||
import { computed, inject, ref, watchEffect } from "vue";
|
||||
import { type CameraSettingsChangeRequest, ValidQuirks } from "@/types/SettingTypes";
|
||||
import axios from "axios";
|
||||
|
||||
const tempSettingsStruct = ref<CameraSettingsChangeRequest>({
|
||||
fov: useCameraSettingsStore().currentCameraSettings.fov.value,
|
||||
@@ -108,6 +109,49 @@ watchEffect(() => {
|
||||
// Reset temp settings on remote camera settings change
|
||||
resetTempSettingsStruct();
|
||||
});
|
||||
|
||||
const showDeleteCamera = ref(false);
|
||||
|
||||
const address = inject<string>("backendHost");
|
||||
const exportSettings = ref();
|
||||
const openExportSettingsPrompt = () => {
|
||||
exportSettings.value.click();
|
||||
};
|
||||
|
||||
const yesDeleteMySettingsText = ref("");
|
||||
const deleteThisCamera = () => {
|
||||
const payload = {
|
||||
cameraUniqueName: useCameraSettingsStore().cameraUniqueNames[useStateStore().currentCameraIndex]
|
||||
};
|
||||
|
||||
axios
|
||||
.post("/utils/nukeOneCamera", payload)
|
||||
.then(() => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Successfully dispatched the delete command. Waiting for backend to start back up",
|
||||
color: "success"
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "The backend is unable to fulfil the request to delete this camera.",
|
||||
color: "error"
|
||||
});
|
||||
} else if (error.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while trying to process the request! The backend didn't respond.",
|
||||
color: "error"
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "An error occurred while trying to process the request.",
|
||||
color: "error"
|
||||
});
|
||||
}
|
||||
});
|
||||
showDeleteCamera.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -144,17 +188,82 @@ watchEffect(() => {
|
||||
:select-cols="8"
|
||||
/>
|
||||
<br />
|
||||
<v-btn
|
||||
class="mt-2 mb-3"
|
||||
style="width: 100%"
|
||||
small
|
||||
color="secondary"
|
||||
:disabled="!settingsHaveChanged()"
|
||||
@click="saveCameraSettings"
|
||||
>
|
||||
<v-icon left> mdi-content-save </v-icon>
|
||||
Save Changes
|
||||
</v-btn>
|
||||
<v-row>
|
||||
<v-col cols="6">
|
||||
<v-btn
|
||||
class="mt-2 mb-3"
|
||||
style="width: 100%"
|
||||
small
|
||||
color="secondary"
|
||||
:disabled="!settingsHaveChanged()"
|
||||
@click="saveCameraSettings"
|
||||
>
|
||||
<v-icon left> mdi-content-save </v-icon>
|
||||
Save Changes
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col cols="6">
|
||||
<v-btn class="mt-2 mb-3" style="width: 100%" small color="red" @click="() => (showDeleteCamera = true)">
|
||||
<v-icon left> mdi-bomb </v-icon>
|
||||
Delete Camera
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<v-dialog v-model="showDeleteCamera" dark width="1500">
|
||||
<v-card dark class="dialog-container pa-6" color="primary" flat>
|
||||
<v-card-title
|
||||
>Delete camera "{{ useCameraSettingsStore().cameraNames[useStateStore().currentCameraIndex] }}"</v-card-title
|
||||
>
|
||||
<v-row class="pl-3 align-center pa-6">
|
||||
<v-col cols="12" md="6">
|
||||
<span class="mt-3"> This will delete ALL OF YOUR SETTINGS and restart PhotonVision. </span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-btn color="secondary" style="float: right" @click="openExportSettingsPrompt">
|
||||
<v-icon left class="open-icon"> mdi-export </v-icon>
|
||||
<span class="open-label">Backup Settings</span>
|
||||
<a
|
||||
ref="exportSettings"
|
||||
style="color: black; text-decoration: none; display: none"
|
||||
:href="`http://${address}/api/settings/photonvision_config.zip`"
|
||||
download="photonvision-settings.zip"
|
||||
target="_blank"
|
||||
/>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-divider class="mt-4 mb-4" />
|
||||
<v-row class="pl-3 align-center pa-6">
|
||||
<v-col>
|
||||
<pv-input
|
||||
v-model="yesDeleteMySettingsText"
|
||||
:label="'Type "' + useCameraSettingsStore().currentCameraName + '":'"
|
||||
:label-cols="12"
|
||||
:input-cols="12"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-btn
|
||||
color="red"
|
||||
:disabled="
|
||||
yesDeleteMySettingsText.toLowerCase() !== useCameraSettingsStore().currentCameraName.toLowerCase()
|
||||
"
|
||||
@click="deleteThisCamera"
|
||||
>
|
||||
<v-icon left class="open-icon"> mdi-skull </v-icon>
|
||||
<span class="open-label">DELETE (UNRECOVERABLE)</span>
|
||||
</v-btn>
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.v-divider {
|
||||
border-color: white !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -201,6 +201,39 @@ const handleSettingsImport = () => {
|
||||
importType.value = -1;
|
||||
importFile.value = null;
|
||||
};
|
||||
|
||||
const showFactoryReset = ref(false);
|
||||
const expected = "Delete Everything";
|
||||
const yesDeleteMySettingsText = ref("");
|
||||
const nukePhotonConfigDirectory = () => {
|
||||
axios
|
||||
.post("/utils/nukeConfigDirectory")
|
||||
.then(() => {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Successfully dispatched the reset command. Waiting for backend to start back up",
|
||||
color: "success"
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "The backend is unable to fulfil the request to reset the device.",
|
||||
color: "error"
|
||||
});
|
||||
} else if (error.request) {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "Error while trying to process the request! The backend didn't respond.",
|
||||
color: "error"
|
||||
});
|
||||
} else {
|
||||
useStateStore().showSnackbarMessage({
|
||||
message: "An error occurred while trying to process the request.",
|
||||
color: "error"
|
||||
});
|
||||
}
|
||||
});
|
||||
showFactoryReset.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -322,11 +355,85 @@ const handleSettingsImport = () => {
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-divider style="margin: 12px 0" />
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-btn color="red" @click="() => (showFactoryReset = true)">
|
||||
<v-icon left class="open-icon"> mdi-skull-crossbones </v-icon>
|
||||
<span class="open-icon">
|
||||
{{
|
||||
$vuetify.breakpoint.mdAndUp
|
||||
? "Factory Reset PhotonVision and delete EVERYTHING (big scary button)"
|
||||
: "Factory Reset PhotonVision"
|
||||
}}
|
||||
</span>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<v-dialog v-model="showFactoryReset" width="1500" dark>
|
||||
<v-card dark class="dialog-container pa-6" color="primary" flat>
|
||||
<v-card-title>
|
||||
<span class="open-label">
|
||||
<v-icon right color="red" class="open-icon">mdi-nuke</v-icon>
|
||||
Factory Reset PhotonVision
|
||||
<v-icon right color="red" class="open-icon">mdi-nuke</v-icon>
|
||||
</span>
|
||||
</v-card-title>
|
||||
|
||||
<v-row class="pl-3 align-center pa-6">
|
||||
<v-col cols="12" md="6">
|
||||
<span class="mt-3"> This will delete ALL OF YOUR SETTINGS and restart PhotonVision. </span>
|
||||
</v-col>
|
||||
<v-col cols="12" md="6">
|
||||
<v-btn color="secondary" style="float: right" @click="openExportSettingsPrompt">
|
||||
<v-icon left class="open-icon"> mdi-export </v-icon>
|
||||
<span class="open-label">Backup Settings</span>
|
||||
<a
|
||||
ref="exportSettings"
|
||||
style="color: black; text-decoration: none; display: none"
|
||||
:href="`http://${address}/api/settings/photonvision_config.zip`"
|
||||
download="photonvision-settings.zip"
|
||||
target="_blank"
|
||||
/>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-divider class="mt-4 mb-4" />
|
||||
|
||||
<v-row class="pl-3 align-center pa-6">
|
||||
<v-col>
|
||||
<pv-input
|
||||
v-model="yesDeleteMySettingsText"
|
||||
:label="'Type "' + expected + '":'"
|
||||
:label-cols="2"
|
||||
:input-cols="10"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-btn
|
||||
color="red"
|
||||
:disabled="yesDeleteMySettingsText.toLowerCase() !== expected.toLowerCase()"
|
||||
@click="nukePhotonConfigDirectory"
|
||||
>
|
||||
<v-icon left class="open-icon"> mdi-skull-crossbones </v-icon>
|
||||
<span class="open-label">
|
||||
{{ $vuetify.breakpoint.mdAndUp ? "Delete everything, I have backed up what I need" : "Delete Everything" }}
|
||||
</span>
|
||||
</v-btn>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dialog-container {
|
||||
min-height: 300px !important;
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
border-color: white !important;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
|
||||
cameraNames(): string[] {
|
||||
return this.cameras.map((c) => c.nickname);
|
||||
},
|
||||
cameraUniqueNames(): string[] {
|
||||
return this.cameras.map((c) => c.nickname);
|
||||
},
|
||||
currentCameraName(): string {
|
||||
return this.cameraNames[useStateStore().currentCameraIndex];
|
||||
},
|
||||
|
||||
@@ -54,6 +54,7 @@ public class ConfigManager {
|
||||
// special case flag to disable flushing settings to disk at shutdown. Avoids the jvm shutdown
|
||||
// hook overwriting the settings we just uploaded
|
||||
private boolean flushOnShutdown = true;
|
||||
private boolean allowWriteTask = false;
|
||||
|
||||
enum ConfigSaveStrategy {
|
||||
SQL,
|
||||
@@ -135,6 +136,10 @@ public class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean nukeConfigDirectory() {
|
||||
return FileUtils.deleteDirectory(getRootFolder());
|
||||
}
|
||||
|
||||
public static boolean saveUploadedSettingsZip(File uploadPath) {
|
||||
// Unpack to /tmp/something/photonvision
|
||||
var folderPath = Path.of(System.getProperty("java.io.tmpdir"), "photonvision").toFile();
|
||||
@@ -142,7 +147,9 @@ public class ConfigManager {
|
||||
ZipUtil.unpack(uploadPath, folderPath);
|
||||
|
||||
// Nuke the current settings directory
|
||||
FileUtils.deleteDirectory(getRootFolder());
|
||||
if (!nukeConfigDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there's a cameras folder in the upload, we know we need to import from the
|
||||
// old style
|
||||
@@ -250,7 +257,14 @@ public class ConfigManager {
|
||||
return imgFilePath.toPath();
|
||||
}
|
||||
|
||||
public Path getCalibrationImageSavePath(Size frameSize, String uniqueCameraName) {
|
||||
public Path getCalibrationImageSavePath(String uniqueCameraName) {
|
||||
var imgFilePath =
|
||||
Path.of(configDirectoryFile.toString(), "calibration", uniqueCameraName).toFile();
|
||||
if (!imgFilePath.exists()) imgFilePath.mkdirs();
|
||||
return imgFilePath.toPath();
|
||||
}
|
||||
|
||||
public Path getCalibrationImageSavePathWithRes(Size frameSize, String uniqueCameraName) {
|
||||
var imgFilePath =
|
||||
Path.of(
|
||||
configDirectoryFile.toString(),
|
||||
@@ -301,7 +315,9 @@ public class ConfigManager {
|
||||
private void saveAndWriteTask() {
|
||||
// Only save if 1 second has past since the request was made
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
if (saveRequestTimestamp > 0 && (System.currentTimeMillis() - saveRequestTimestamp) > 1000L) {
|
||||
if (saveRequestTimestamp > 0
|
||||
&& (System.currentTimeMillis() - saveRequestTimestamp) > 1000L
|
||||
&& allowWriteTask) {
|
||||
saveRequestTimestamp = -1;
|
||||
logger.debug("Saving to disk...");
|
||||
saveToDisk();
|
||||
@@ -330,6 +346,11 @@ public class ConfigManager {
|
||||
this.flushOnShutdown = false;
|
||||
}
|
||||
|
||||
/** Prevent pending automatic saves */
|
||||
public void setWriteTaskEnabled(boolean enabled) {
|
||||
this.allowWriteTask = enabled;
|
||||
}
|
||||
|
||||
public void onJvmExit() {
|
||||
if (flushOnShutdown) {
|
||||
logger.info("Force-flushing settings...");
|
||||
|
||||
@@ -114,6 +114,16 @@ public class PhotonConfiguration {
|
||||
cameraConfigurations.put(name, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a camera by its unique name
|
||||
*
|
||||
* @param name The camera name (usually unique name)
|
||||
* @return True if the camera configuration was removed
|
||||
*/
|
||||
public boolean removeCameraConfig(String name) {
|
||||
return cameraConfigurations.remove(name) != null;
|
||||
}
|
||||
|
||||
public Map<String, Object> toHashMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
var settingsSubmap = new HashMap<String, Object>();
|
||||
|
||||
@@ -349,6 +349,19 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
|
||||
private void saveCameras(Connection conn) {
|
||||
try {
|
||||
// Delete all cameras we don't need anymore
|
||||
String deleteExtraCamsString =
|
||||
String.format(
|
||||
"DELETE FROM %s WHERE %s not in (%s)",
|
||||
Tables.CAMERAS,
|
||||
Columns.CAM_UNIQUE_NAME,
|
||||
config.getCameraConfigurations().keySet().stream()
|
||||
.map(it -> "\"" + it + "\"")
|
||||
.collect(Collectors.joining(", ")));
|
||||
|
||||
var stmt = conn.createStatement();
|
||||
stmt.executeUpdate(deleteExtraCamsString);
|
||||
|
||||
// Replace this camera's row with the new settings
|
||||
var sqlString =
|
||||
String.format(
|
||||
@@ -388,6 +401,7 @@ public class SqlConfigProvider extends ConfigProvider {
|
||||
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
} catch (SQLException | IOException e) {
|
||||
logger.error("Err saving cameras", e);
|
||||
try {
|
||||
|
||||
@@ -123,6 +123,11 @@ public class Logger {
|
||||
currentAppenders.add(new FileLogAppender(logFilePath));
|
||||
}
|
||||
|
||||
public static void closeAllLoggers() {
|
||||
currentAppenders.forEach(LogAppender::shutdown);
|
||||
currentAppenders.clear();
|
||||
}
|
||||
|
||||
public static void cleanLogs(Path folderToClean) {
|
||||
File[] logs = folderToClean.toFile().listFiles();
|
||||
if (logs == null) return;
|
||||
@@ -284,6 +289,9 @@ public class Logger {
|
||||
|
||||
private interface LogAppender {
|
||||
void log(String message, LogLevel level);
|
||||
|
||||
/** Release any file or other resources currently held by the Logger */
|
||||
default void shutdown() {}
|
||||
}
|
||||
|
||||
private static class ConsoleLogAppender implements LogAppender {
|
||||
@@ -343,5 +351,16 @@ public class Logger {
|
||||
// Nothing to do - no stream available for writing
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
try {
|
||||
out.close();
|
||||
out = null;
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class FileUtils {
|
||||
private static final Set<PosixFilePermission> allReadWriteExecutePerms =
|
||||
new HashSet<>(Arrays.asList(PosixFilePermission.values()));
|
||||
|
||||
public static void deleteDirectory(Path path) {
|
||||
public static boolean deleteDirectory(Path path) {
|
||||
try {
|
||||
var files = Files.walk(path);
|
||||
|
||||
@@ -53,8 +53,11 @@ public class FileUtils {
|
||||
|
||||
// close the stream
|
||||
files.close();
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
logger.error("Exception deleting files in " + path + "!", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -390,7 +390,7 @@ public class VisionModule {
|
||||
var ret =
|
||||
pipelineManager.calibration3dPipeline.tryCalibration(
|
||||
ConfigManager.getInstance()
|
||||
.getCalibrationImageSavePath(
|
||||
.getCalibrationImageSavePathWithRes(
|
||||
pipelineManager.calibration3dPipeline.getSettings().resolution,
|
||||
visionSource.getCameraConfiguration().uniqueName));
|
||||
pipelineManager.setCalibrationMode(false);
|
||||
|
||||
@@ -255,7 +255,8 @@ public class Calibrate3dPipeTest {
|
||||
|
||||
var cal =
|
||||
calibration3dPipeline.tryCalibration(
|
||||
ConfigManager.getInstance().getCalibrationImageSavePath(imgRes, "Calibration_Test"));
|
||||
ConfigManager.getInstance()
|
||||
.getCalibrationImageSavePathWithRes(imgRes, "Calibration_Test"));
|
||||
calibration3dPipeline.finishCalibration();
|
||||
|
||||
// visuallyDebugDistortion(directoryListing, imgRes, cal );
|
||||
|
||||
@@ -94,16 +94,18 @@ public class RequestHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigManager.getInstance().setWriteTaskEnabled(false);
|
||||
ConfigManager.getInstance().disableFlushOnShutdown();
|
||||
// We want to delete the -whole- zip file, so we need to teardown loggers for now
|
||||
logger.info("Writing new settings zip (logs may be truncated)...");
|
||||
Logger.closeAllLoggers();
|
||||
if (ConfigManager.saveUploadedSettingsZip(tempFilePath.get())) {
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully saved the uploaded settings zip, rebooting...");
|
||||
logger.info("Successfully saved the uploaded settings zip, rebooting...");
|
||||
ConfigManager.getInstance().disableFlushOnShutdown();
|
||||
restartProgram();
|
||||
} else {
|
||||
ctx.status(500);
|
||||
ctx.result("There was an error while saving the uploaded zip file");
|
||||
logger.error("There was an error while saving the uploaded zip file");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,4 +773,46 @@ public class RequestHandler {
|
||||
},
|
||||
0);
|
||||
}
|
||||
|
||||
public static void onNukeConfigDirectory(Context ctx) {
|
||||
ConfigManager.getInstance().setWriteTaskEnabled(false);
|
||||
ConfigManager.getInstance().disableFlushOnShutdown();
|
||||
|
||||
Logger.closeAllLoggers();
|
||||
if (ConfigManager.nukeConfigDirectory()) {
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully nuked config dir");
|
||||
restartProgram();
|
||||
} else {
|
||||
ctx.status(500);
|
||||
ctx.result("There was an error while nuking the config directory");
|
||||
}
|
||||
}
|
||||
|
||||
public static void onNukeOneCamera(Context ctx) {
|
||||
try {
|
||||
var payload = kObjectMapper.readTree(ctx.bodyInputStream());
|
||||
var name = payload.get("cameraUniqueName").asText();
|
||||
logger.warn("Deleting camera name " + name);
|
||||
|
||||
var cameraDir = ConfigManager.getInstance().getCalibrationImageSavePath(name).toFile();
|
||||
if (cameraDir.exists()) {
|
||||
FileUtils.deleteDirectory(cameraDir);
|
||||
}
|
||||
|
||||
// prevent -anyone- else from writing camera configs -- but flush first
|
||||
ConfigManager.getInstance().saveToDisk();
|
||||
ConfigManager.getInstance().setWriteTaskEnabled(false);
|
||||
ConfigManager.getInstance().disableFlushOnShutdown();
|
||||
// remove the config from the global config and force-flush
|
||||
ConfigManager.getInstance().getConfig().removeCameraConfig(name);
|
||||
ConfigManager.getInstance().saveToDisk();
|
||||
ctx.status(200);
|
||||
restartProgram();
|
||||
} catch (IOException e) {
|
||||
// todo
|
||||
logger.error("asdf", e);
|
||||
ctx.status(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ public class Server {
|
||||
app.get("/api/utils/getImageSnapshots", RequestHandler::onImageSnapshotsRequest);
|
||||
app.get("/api/utils/getCalSnapshot", RequestHandler::onCalibrationSnapshotRequest);
|
||||
app.get("/api/utils/getCalibrationJSON", RequestHandler::onCalibrationExportRequest);
|
||||
app.post("/api/utils/nukeConfigDirectory", RequestHandler::onNukeConfigDirectory);
|
||||
app.post("/api/utils/nukeOneCamera", RequestHandler::onNukeOneCamera);
|
||||
|
||||
// Calibration
|
||||
app.post("/api/calibration/end", RequestHandler::onCalibrationEndRequest);
|
||||
|
||||
Reference in New Issue
Block a user