mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
Standardize API (#1942)
## Description closes #1941 Rewrite the API to use the payload method , and use records in the ``RequestHandler``. There's a couple places where this isn't the place, that's where the front end is making a get request, and so a payload doesn't make sense. This is meant to encourage more static typing. Additionally, fix some typos in params in ``CameraSettingsStore`` ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [x] If this PR changes behavior or adds a feature, user documentation is updated - [x] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [x] If this PR touches configuration, this is backwards compatible with settings back to v2024.3.1 - [x] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [x] If this PR addresses a bug, a regression test for it is added - [x] Everything changed got tested --------- Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.photonvision.server;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.javalin.http.Context;
|
||||
@@ -51,7 +50,6 @@ import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.networking.NetworkManager;
|
||||
import org.photonvision.common.util.ShellExec;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.common.util.file.JacksonUtils;
|
||||
import org.photonvision.common.util.file.ProgramDirectoryUtilities;
|
||||
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
|
||||
import org.photonvision.vision.camera.CameraQuirk;
|
||||
@@ -68,6 +66,8 @@ public class RequestHandler {
|
||||
|
||||
private static final ObjectMapper kObjectMapper = new ObjectMapper();
|
||||
|
||||
private record CommonCameraUniqueName(String cameraUniqueName) {}
|
||||
|
||||
public static void onSettingsImportRequest(Context ctx) {
|
||||
var file = ctx.uploadedFile("data");
|
||||
|
||||
@@ -380,39 +380,38 @@ public class RequestHandler {
|
||||
NetworkTablesManager.getInstance().setConfig(config);
|
||||
}
|
||||
|
||||
public static class UICameraSettingsRequest {
|
||||
@JsonProperty("fov")
|
||||
double fov;
|
||||
|
||||
@JsonProperty("quirksToChange")
|
||||
HashMap<CameraQuirk, Boolean> quirksToChange;
|
||||
}
|
||||
private record CameraSettingsRequest(
|
||||
double fov, HashMap<CameraQuirk, Boolean> quirksToChange, String cameraUniqueName) {}
|
||||
|
||||
public static void onCameraSettingsRequest(Context ctx) {
|
||||
try {
|
||||
var data = kObjectMapper.readTree(ctx.bodyInputStream());
|
||||
CameraSettingsRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CameraSettingsRequest.class);
|
||||
// Extract the settings from the request
|
||||
double fov = request.fov;
|
||||
HashMap<CameraQuirk, Boolean> quirksToChange = request.quirksToChange;
|
||||
String cameraUniqueName = request.cameraUniqueName;
|
||||
|
||||
String cameraUniqueName = data.get("cameraUniqueName").asText();
|
||||
var settings =
|
||||
JacksonUtils.deserialize(data.get("settings").toString(), UICameraSettingsRequest.class);
|
||||
var fov = settings.fov;
|
||||
if (cameraUniqueName == null || cameraUniqueName.isEmpty()) {
|
||||
ctx.status(400).result("cameraUniqueName is required");
|
||||
logger.error("cameraUniqueName is missing in the request");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Changing camera FOV to: " + fov);
|
||||
logger.info("Changing quirks to: " + settings.quirksToChange.toString());
|
||||
|
||||
logger.info("Changing quirks to: " + quirksToChange.toString());
|
||||
|
||||
var module = VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName);
|
||||
module.setFov(fov);
|
||||
module.changeCameraQuirks(settings.quirksToChange);
|
||||
|
||||
module.setFov(fov);
|
||||
module.changeCameraQuirks(quirksToChange);
|
||||
module.saveModule();
|
||||
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully saved camera settings");
|
||||
logger.info("Successfully saved camera settings");
|
||||
} catch (NullPointerException | IOException e) {
|
||||
ctx.status(400);
|
||||
ctx.result("The provided camera settings were malformed");
|
||||
logger.error("The provided camera settings were malformed", e);
|
||||
ctx.status(200).result("Camera settings updated successfully");
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to process camera settings request", e);
|
||||
ctx.status(500).result("Failed to process camera settings request");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,20 +473,21 @@ public class RequestHandler {
|
||||
public static void onCalibrationEndRequest(Context ctx) {
|
||||
logger.info("Calibrating camera! This will take a long time...");
|
||||
|
||||
String cameraUniqueName;
|
||||
|
||||
try {
|
||||
cameraUniqueName =
|
||||
kObjectMapper.readTree(ctx.bodyInputStream()).get("cameraUniqueName").asText();
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
|
||||
var calData =
|
||||
VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).endCalibration();
|
||||
VisionSourceManager.getInstance()
|
||||
.vmm
|
||||
.getModule(request.cameraUniqueName)
|
||||
.endCalibration();
|
||||
if (calData == null) {
|
||||
ctx.result("The calibration process failed");
|
||||
ctx.status(500);
|
||||
logger.error(
|
||||
"The calibration process failed. Calibration data for module at cameraUniqueName ("
|
||||
+ cameraUniqueName
|
||||
+ request.cameraUniqueName
|
||||
+ ") was null");
|
||||
return;
|
||||
}
|
||||
@@ -509,20 +509,20 @@ public class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private record DataCalibrationImportRequest(
|
||||
String cameraUniqueName, CameraCalibrationCoefficients calibration) {}
|
||||
|
||||
public static void onDataCalibrationImportRequest(Context ctx) {
|
||||
try {
|
||||
var data = kObjectMapper.readTree(ctx.bodyInputStream());
|
||||
|
||||
String cameraUniqueName = data.get("cameraUniqueName").asText();
|
||||
var coeffs =
|
||||
kObjectMapper.convertValue(data.get("calibration"), CameraCalibrationCoefficients.class);
|
||||
DataCalibrationImportRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), DataCalibrationImportRequest.class);
|
||||
|
||||
var uploadCalibrationEvent =
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"calibrationUploaded",
|
||||
coeffs,
|
||||
cameraUniqueName,
|
||||
request.calibration,
|
||||
request.cameraUniqueName,
|
||||
null);
|
||||
DataChangeService.getInstance().publishEvent(uploadCalibrationEvent);
|
||||
|
||||
@@ -612,26 +612,26 @@ public class RequestHandler {
|
||||
ctx.status(HardwareManager.getInstance().restartDevice() ? 204 : 500);
|
||||
}
|
||||
|
||||
private record CameraNicknameChangeRequest(String name, String cameraUniqueName) {}
|
||||
|
||||
public static void onCameraNicknameChangeRequest(Context ctx) {
|
||||
try {
|
||||
var data = kObjectMapper.readTree(ctx.bodyInputStream());
|
||||
CameraNicknameChangeRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CameraNicknameChangeRequest.class);
|
||||
|
||||
String name = data.get("name").asText();
|
||||
String cameraUniqueName = data.get("cameraUniqueName").asText();
|
||||
|
||||
VisionSourceManager.getInstance().vmm.getModule(cameraUniqueName).setCameraNickname(name);
|
||||
VisionSourceManager.getInstance()
|
||||
.vmm
|
||||
.getModule(request.cameraUniqueName)
|
||||
.setCameraNickname(request.name);
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully changed the camera name to: " + name);
|
||||
logger.info("Successfully changed the camera name to: " + name);
|
||||
ctx.result("Successfully changed the camera name to: " + request.name);
|
||||
logger.info("Successfully changed the camera name to: " + request.name);
|
||||
} catch (JsonProcessingException e) {
|
||||
ctx.status(400);
|
||||
ctx.result("The provided nickname data was malformed");
|
||||
logger.error("The provided nickname data was malformed", e);
|
||||
|
||||
ctx.status(400).result("Invalid JSON format");
|
||||
logger.error("Failed to process camera nickname change request", e);
|
||||
} catch (Exception e) {
|
||||
ctx.status(500);
|
||||
ctx.result("An error occurred while changing the camera's nickname");
|
||||
logger.error("An error occurred while changing the camera's nickname", e);
|
||||
ctx.status(500).result("Failed to change camera nickname");
|
||||
logger.error("Unexpected error while changing camera nickname", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,12 +641,10 @@ public class RequestHandler {
|
||||
}
|
||||
|
||||
public static void onCalibrationSnapshotRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
|
||||
String cameraUniqueName = ctx.queryParam("cameraUniqueName");
|
||||
var width = Integer.parseInt(ctx.queryParam("width"));
|
||||
var height = Integer.parseInt(ctx.queryParam("height"));
|
||||
var observationIdx = Integer.parseInt(ctx.queryParam("snapshotIdx"));
|
||||
Integer observationIdx = Integer.parseInt(ctx.queryParam("snapshotIdx"));
|
||||
|
||||
CameraCalibrationCoefficients calList =
|
||||
VisionSourceManager.getInstance()
|
||||
@@ -695,8 +693,6 @@ public class RequestHandler {
|
||||
}
|
||||
|
||||
public static void onCalibrationExportRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
|
||||
String cameraUniqueName = ctx.queryParam("cameraUniqueName");
|
||||
var width = Integer.parseInt(ctx.queryParam("width"));
|
||||
var height = Integer.parseInt(ctx.queryParam("height"));
|
||||
@@ -880,72 +876,97 @@ public class RequestHandler {
|
||||
|
||||
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);
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
|
||||
var cameraDir = ConfigManager.getInstance().getCalibrationImageSavePath(name).toFile();
|
||||
logger.warn("Deleting camera name " + request.cameraUniqueName);
|
||||
|
||||
var cameraDir =
|
||||
ConfigManager.getInstance()
|
||||
.getCalibrationImageSavePath(request.cameraUniqueName)
|
||||
.toFile();
|
||||
if (cameraDir.exists()) {
|
||||
FileUtils.deleteDirectory(cameraDir);
|
||||
}
|
||||
|
||||
VisionSourceManager.getInstance().deleteVisionSource(name);
|
||||
VisionSourceManager.getInstance().deleteVisionSource(request.cameraUniqueName);
|
||||
|
||||
ctx.status(200);
|
||||
} catch (IOException e) {
|
||||
// todo
|
||||
logger.error("asdf", e);
|
||||
logger.error("Failed to delete camera", e);
|
||||
ctx.status(500);
|
||||
ctx.result("Failed to delete camera");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void onActivateMatchedCameraRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
|
||||
String cameraUniqueName = ctx.queryParam("cameraUniqueName");
|
||||
|
||||
if (VisionSourceManager.getInstance().reactivateDisabledCameraConfig(cameraUniqueName)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(403);
|
||||
if (VisionSourceManager.getInstance()
|
||||
.reactivateDisabledCameraConfig(request.cameraUniqueName)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(403);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process activate matched camera request", e);
|
||||
ctx.result("Failed to process activate matched camera request");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.result("Successfully assigned camera with unique name: " + cameraUniqueName);
|
||||
}
|
||||
|
||||
private record AssignUnmatchedCamera(PVCameraInfo cameraInfo) {}
|
||||
|
||||
public static void onAssignUnmatchedCameraRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
|
||||
PVCameraInfo camera;
|
||||
try {
|
||||
camera = JacksonUtils.deserialize(ctx.queryParam("cameraInfo"), PVCameraInfo.class);
|
||||
AssignUnmatchedCamera request =
|
||||
kObjectMapper.readValue(ctx.body(), AssignUnmatchedCamera.class);
|
||||
|
||||
if (request.cameraInfo == null) {
|
||||
ctx.status(400);
|
||||
ctx.result("cameraInfo is required");
|
||||
logger.error("cameraInfo is missing in the request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (VisionSourceManager.getInstance().assignUnmatchedCamera(request.cameraInfo)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(404);
|
||||
}
|
||||
|
||||
ctx.result("Successfully assigned camera: " + request.cameraInfo);
|
||||
} catch (IOException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process assign unmatched camera request", e);
|
||||
ctx.result("Failed to process assign unmatched camera request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (VisionSourceManager.getInstance().assignUnmatchedCamera(camera)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(404);
|
||||
}
|
||||
|
||||
ctx.result("Successfully assigned camera: " + camera);
|
||||
}
|
||||
|
||||
public static void onUnassignCameraRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
|
||||
String cameraUniqueName = ctx.queryParam("cameraUniqueName");
|
||||
|
||||
if (VisionSourceManager.getInstance().deactivateVisionSource(cameraUniqueName)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(403);
|
||||
if (VisionSourceManager.getInstance().deactivateVisionSource(request.cameraUniqueName)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(403);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process unassign camera request", e);
|
||||
ctx.result("Failed to process unassign camera request");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.status(200);
|
||||
|
||||
ctx.result("Successfully assigned camera with unique name: " + cameraUniqueName);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user