mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-28 02:11:40 +00:00
Switch from FasterXML Jackson to Avaje Jsonb (#2503)
## Description WPILib switched from FasterXML Jackson to Avaje Jsonb for speed reasons in https://github.com/wpilibsuite/allwpilib/pull/8721. This does the same for PhotonVision. Some temporary Jackson adapters are present to allow compatibility with alpha-4 ahead of updating Photon's WPILib version. A few old backwards compatibility migrations were also dropped if they were difficult to port to Avaje Jsonb or otherwise complicated the code. ## 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_, including events that led to this PR - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with all settings going back to the previous seasons's last release (seasons end after champs ends) - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added - [ ] If this PR adds a dependency, the license has been checked for compatibility and steps taken to follow it --------- Co-authored-by: samfreund <samf.236@proton.me> Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
This commit is contained in:
@@ -235,6 +235,8 @@ public class Main {
|
||||
Logger.setLevel(LogGroup.General, logLevel);
|
||||
logger.info("Logging initialized in debug mode.");
|
||||
|
||||
System.setProperty("jsonb.disableAdapterSpi", "true");
|
||||
|
||||
logger.info(
|
||||
"Starting PhotonVision version "
|
||||
+ PhotonVersion.versionString
|
||||
|
||||
@@ -17,22 +17,21 @@
|
||||
|
||||
package org.photonvision.server;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.avaje.json.JsonException;
|
||||
import io.avaje.jsonb.Json;
|
||||
import io.avaje.jsonb.Jsonb;
|
||||
import io.avaje.jsonb.jackson.JacksonAdapter;
|
||||
import io.javalin.websocket.WsBinaryMessageContext;
|
||||
import io.javalin.websocket.WsCloseContext;
|
||||
import io.javalin.websocket.WsConnectContext;
|
||||
import io.javalin.websocket.WsContext;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.msgpack.jackson.dataformat.MessagePackFactory;
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
@@ -47,7 +46,9 @@ import org.wpilib.math.util.Pair;
|
||||
public class DataSocketHandler {
|
||||
private final Logger logger = new Logger(DataSocketHandler.class, LogGroup.WebServer);
|
||||
private final List<WsContext> users = new CopyOnWriteArrayList<>();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
|
||||
private final JacksonAdapter adapter =
|
||||
JacksonAdapter.builder().jsonFactory(new MessagePackFactory()).serializeEmpty(true).build();
|
||||
private final Jsonb msgpackJsonb = Jsonb.builder().adapter(adapter).build();
|
||||
private final DataChangeService dcService = DataChangeService.getInstance();
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
@@ -91,20 +92,16 @@ public class DataSocketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Json
|
||||
static record WSMessage(
|
||||
@Nullable String cameraUniqueName, @Json.Unmapped Map<String, Object> properties) {}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public void onBinaryMessage(WsBinaryMessageContext context) {
|
||||
try {
|
||||
Map<String, Object> deserializedData =
|
||||
objectMapper.readValue(context.data(), new TypeReference<>() {});
|
||||
var message = msgpackJsonb.type(WSMessage.class).fromJson(context.data());
|
||||
|
||||
// Special case the current camera index
|
||||
String cameraUniqueName = "";
|
||||
if (deserializedData.get("cameraUniqueName") instanceof String camUniqueNameValue) {
|
||||
cameraUniqueName = camUniqueNameValue;
|
||||
deserializedData.remove("cameraUniqueName");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : deserializedData.entrySet()) {
|
||||
for (Map.Entry<String, Object> entry : message.properties.entrySet()) {
|
||||
try {
|
||||
var entryKey = entry.getKey();
|
||||
var entryValue = entry.getValue();
|
||||
@@ -131,7 +128,7 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"isDriverMode",
|
||||
(Boolean) entryValue,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_CHANGECAMERANAME ->
|
||||
dcService.publishEvent(
|
||||
@@ -139,7 +136,7 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"cameraNickname",
|
||||
(String) entryValue,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_CHANGEPIPELINENAME ->
|
||||
dcService.publishEvent(
|
||||
@@ -147,38 +144,39 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"pipelineName",
|
||||
(String) entryValue,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_ADDNEWPIPELINE -> {
|
||||
// HashMap<String, Object> data = (HashMap<String, Object>) entryValue;
|
||||
// var type = (PipelineType) data.get("pipelineType");
|
||||
// var name = (String) data.get("pipelineName");
|
||||
var arr = (ArrayList<Object>) entryValue;
|
||||
var arr = (List<Object>) entryValue;
|
||||
var name = (String) arr.get(0);
|
||||
var type = PipelineType.values()[(Integer) arr.get(1) + 3];
|
||||
var type = PipelineType.values()[((Long) arr.get(1)).intValue() + 3];
|
||||
|
||||
dcService.publishEvent(
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"newPipelineInfo",
|
||||
Pair.of(name, type),
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
}
|
||||
case SMT_CHANGEBRIGHTNESS ->
|
||||
HardwareManager.getInstance()
|
||||
.setBrightnessPercent(Integer.parseInt(entryValue.toString()));
|
||||
case SMT_DUPLICATEPIPELINE -> {
|
||||
var pipeIndex = (Integer) entryValue;
|
||||
var pipeIndex = ((Long) entryValue).intValue();
|
||||
|
||||
logger.info("Duplicating pipe@index" + pipeIndex + " for camera " + cameraUniqueName);
|
||||
logger.info(
|
||||
"Duplicating pipe@index" + pipeIndex + " for camera " + message.cameraUniqueName);
|
||||
|
||||
dcService.publishEvent(
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"duplicatePipeline",
|
||||
pipeIndex,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
}
|
||||
case SMT_DELETECURRENTPIPELINE ->
|
||||
@@ -187,27 +185,29 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"deleteCurrPipeline",
|
||||
0,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_ROBOTOFFSETPOINT ->
|
||||
dcService.publishEvent(
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"robotOffsetPoint",
|
||||
(Integer) entryValue,
|
||||
cameraUniqueName,
|
||||
((Long) entryValue).intValue(),
|
||||
message.cameraUniqueName,
|
||||
null));
|
||||
case SMT_CURRENTCAMERA ->
|
||||
dcService.publishEvent(
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_OTHER, "changeUICamera", (Integer) entryValue));
|
||||
DataChangeDestination.DCD_OTHER,
|
||||
"changeUICamera",
|
||||
((Long) entryValue).intValue()));
|
||||
case SMT_CURRENTPIPELINE ->
|
||||
dcService.publishEvent(
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"changePipeline",
|
||||
(Integer) entryValue,
|
||||
cameraUniqueName,
|
||||
((Long) entryValue).intValue(),
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_STARTPNPCALIBRATION ->
|
||||
dcService.publishEvent(
|
||||
@@ -215,7 +215,7 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"startCalibration",
|
||||
(Map) entryValue,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_SAVEINPUTSNAPSHOT ->
|
||||
dcService.publishEvent(
|
||||
@@ -223,7 +223,7 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"saveInputSnapshot",
|
||||
0,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_SAVEOUTPUTSNAPSHOT ->
|
||||
dcService.publishEvent(
|
||||
@@ -231,7 +231,7 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"saveOutputSnapshot",
|
||||
0,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_TAKECALIBRATIONSNAPSHOT ->
|
||||
dcService.publishEvent(
|
||||
@@ -239,10 +239,10 @@ public class DataSocketHandler {
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"takeCalSnapshot",
|
||||
0,
|
||||
cameraUniqueName,
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
case SMT_PIPELINESETTINGCHANGE -> {
|
||||
HashMap<String, Object> data = (HashMap<String, Object>) entryValue;
|
||||
Map<String, Object> data = (Map) entryValue;
|
||||
|
||||
if (data.size() >= 2) {
|
||||
var cameraIndex2 = (String) data.get("cameraUniqueName");
|
||||
@@ -267,28 +267,27 @@ public class DataSocketHandler {
|
||||
new IncomingWebSocketEvent<>(
|
||||
DataChangeDestination.DCD_ACTIVEMODULE,
|
||||
"changePipelineType",
|
||||
(Integer) entryValue,
|
||||
cameraUniqueName,
|
||||
((Long) entryValue).intValue(),
|
||||
message.cameraUniqueName,
|
||||
context));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to parse message!", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
logger.error("Failed to deserialize message!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(ByteBuffer b, WsContext user) throws JsonProcessingException {
|
||||
private void sendMessage(ByteBuffer b, WsContext user) {
|
||||
if (user.session.isOpen()) {
|
||||
user.send(b);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastMessage(Object message, WsContext userToSkip)
|
||||
throws JsonProcessingException {
|
||||
ByteBuffer b = ByteBuffer.wrap(objectMapper.writeValueAsBytes(message));
|
||||
public void broadcastMessage(Object message, WsContext userToSkip) throws JsonException {
|
||||
ByteBuffer b = ByteBuffer.wrap(msgpackJsonb.toJsonBytes(message));
|
||||
|
||||
if (userToSkip == null) {
|
||||
for (WsContext user : users) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public enum DataSocketMessageType {
|
||||
SMT_DRIVERMODE("driverMode"),
|
||||
SMT_CHANGECAMERANAME("changeCameraName"),
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
|
||||
package org.photonvision.server;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.avaje.json.JsonException;
|
||||
import io.avaje.jsonb.Json;
|
||||
import io.avaje.jsonb.Jsonb;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.UploadedFile;
|
||||
import java.io.*;
|
||||
@@ -53,7 +54,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;
|
||||
@@ -71,8 +71,6 @@ public class RequestHandler {
|
||||
|
||||
private static final Logger logger = new Logger(RequestHandler.class, LogGroup.WebServer);
|
||||
|
||||
private static final ObjectMapper kObjectMapper = new ObjectMapper();
|
||||
|
||||
private static boolean testMode = false;
|
||||
|
||||
public static void onStatusRequest(Context ctx) {
|
||||
@@ -84,7 +82,8 @@ public class RequestHandler {
|
||||
testMode = isTestMode;
|
||||
}
|
||||
|
||||
private record CommonCameraUniqueName(String cameraUniqueName) {}
|
||||
@Json
|
||||
record CommonCameraUniqueName(String cameraUniqueName) {}
|
||||
|
||||
public static void onSettingsImportRequest(Context ctx) {
|
||||
var file = ctx.uploadedFile("data");
|
||||
@@ -372,12 +371,12 @@ public class RequestHandler {
|
||||
public static void onGeneralSettingsRequest(Context ctx) {
|
||||
NetworkConfig config;
|
||||
try {
|
||||
config = kObjectMapper.readValue(ctx.bodyInputStream(), NetworkConfig.class);
|
||||
config = Jsonb.instance().type(NetworkConfig.class).fromJson(ctx.bodyInputStream());
|
||||
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully saved general settings");
|
||||
logger.info("Successfully saved general settings");
|
||||
} catch (IOException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
// If the settings can't be parsed, use the default network settings
|
||||
config = new NetworkConfig();
|
||||
|
||||
@@ -394,13 +393,14 @@ public class RequestHandler {
|
||||
NetworkTablesManager.getInstance().setConfig(config);
|
||||
}
|
||||
|
||||
private record CameraSettingsRequest(
|
||||
@Json
|
||||
record CameraSettingsRequest(
|
||||
double fov, HashMap<CameraQuirk, Boolean> quirksToChange, String cameraUniqueName) {}
|
||||
|
||||
public static void onCameraSettingsRequest(Context ctx) {
|
||||
try {
|
||||
CameraSettingsRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CameraSettingsRequest.class);
|
||||
Jsonb.instance().type(CameraSettingsRequest.class).fromJson(ctx.body());
|
||||
// Extract the settings from the request
|
||||
double fov = request.fov;
|
||||
HashMap<CameraQuirk, Boolean> quirksToChange = request.quirksToChange;
|
||||
@@ -489,7 +489,7 @@ public class RequestHandler {
|
||||
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
Jsonb.instance().type(CommonCameraUniqueName.class).fromJson(ctx.body());
|
||||
|
||||
var calData =
|
||||
VisionSourceManager.getInstance()
|
||||
@@ -509,7 +509,7 @@ public class RequestHandler {
|
||||
ctx.result("Camera calibration successfully completed!");
|
||||
ctx.status(200);
|
||||
logger.info("Camera calibration successfully completed!");
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(400);
|
||||
ctx.result(
|
||||
"The 'cameraUniqueName' field was not found in the request. Please make sure the cameraUniqueName of the vision module is specified with the 'cameraUniqueName' key.");
|
||||
@@ -523,13 +523,14 @@ public class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private record DataCalibrationImportRequest(
|
||||
@Json
|
||||
record DataCalibrationImportRequest(
|
||||
String cameraUniqueName, CameraCalibrationCoefficients calibration) {}
|
||||
|
||||
public static void onDataCalibrationImportRequest(Context ctx) {
|
||||
try {
|
||||
try (var stream = ctx.req().getInputStream()) {
|
||||
DataCalibrationImportRequest request =
|
||||
kObjectMapper.readValue(ctx.req().getInputStream(), DataCalibrationImportRequest.class);
|
||||
Jsonb.instance().type(DataCalibrationImportRequest.class).fromJson(stream);
|
||||
|
||||
var uploadCalibrationEvent =
|
||||
new IncomingWebSocketEvent<>(
|
||||
@@ -543,7 +544,7 @@ public class RequestHandler {
|
||||
ctx.status(200);
|
||||
ctx.result("Calibration imported successfully from imported data!");
|
||||
logger.info("Calibration imported successfully from imported data!");
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(400);
|
||||
ctx.result("The provided calibration data was malformed");
|
||||
logger.error("The provided calibration data was malformed", e);
|
||||
@@ -704,11 +705,10 @@ public class RequestHandler {
|
||||
}
|
||||
ConfigManager.getInstance()
|
||||
.getConfig()
|
||||
.neuralNetworkPropertyManager()
|
||||
.getNeuralNetworkProperties()
|
||||
.addModelProperties(modelProperties);
|
||||
|
||||
logger.debug(
|
||||
ConfigManager.getInstance().getConfig().neuralNetworkPropertyManager().toString());
|
||||
logger.debug(ConfigManager.getInstance().getConfig().getNeuralNetworkProperties().toString());
|
||||
|
||||
NeuralNetworkModelManager.getInstance().discoverModels();
|
||||
|
||||
@@ -860,14 +860,15 @@ public class RequestHandler {
|
||||
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
|
||||
}
|
||||
|
||||
private record DeleteObjectDetectionModelRequest(Path modelPath) {}
|
||||
@Json
|
||||
record DeleteObjectDetectionModelRequest(Path modelPath) {}
|
||||
|
||||
public static void onDeleteObjectDetectionModelRequest(Context ctx) {
|
||||
logger.info("Deleting object detection model");
|
||||
|
||||
try {
|
||||
DeleteObjectDetectionModelRequest request =
|
||||
JacksonUtils.deserialize(ctx.body(), DeleteObjectDetectionModelRequest.class);
|
||||
Jsonb.instance().type(DeleteObjectDetectionModelRequest.class).fromJson(ctx.body());
|
||||
|
||||
if (request.modelPath == null) {
|
||||
ctx.status(400);
|
||||
@@ -892,7 +893,7 @@ public class RequestHandler {
|
||||
|
||||
if (!ConfigManager.getInstance()
|
||||
.getConfig()
|
||||
.neuralNetworkPropertyManager()
|
||||
.getNeuralNetworkProperties()
|
||||
.removeModel(request.modelPath)) {
|
||||
ctx.status(400);
|
||||
ctx.result("The model's information was not found in the config");
|
||||
@@ -917,12 +918,13 @@ public class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private record RenameObjectDetectionModelRequest(Path modelPath, String newName) {}
|
||||
@Json
|
||||
record RenameObjectDetectionModelRequest(Path modelPath, String newName) {}
|
||||
|
||||
public static void onRenameObjectDetectionModelRequest(Context ctx) {
|
||||
try {
|
||||
RenameObjectDetectionModelRequest request =
|
||||
JacksonUtils.deserialize(ctx.body(), RenameObjectDetectionModelRequest.class);
|
||||
Jsonb.instance().type(RenameObjectDetectionModelRequest.class).fromJson(ctx.body());
|
||||
|
||||
if (request.modelPath == null) {
|
||||
ctx.status(400);
|
||||
@@ -947,7 +949,7 @@ public class RequestHandler {
|
||||
|
||||
if (!ConfigManager.getInstance()
|
||||
.getConfig()
|
||||
.neuralNetworkPropertyManager()
|
||||
.getNeuralNetworkProperties()
|
||||
.renameModel(request.modelPath, request.newName)) {
|
||||
ctx.status(400);
|
||||
ctx.result("The model's information was not found in the config");
|
||||
@@ -994,12 +996,13 @@ public class RequestHandler {
|
||||
ctx.status(HardwareManager.getInstance().restartDevice() ? 204 : 500);
|
||||
}
|
||||
|
||||
private record CameraNicknameChangeRequest(String name, String cameraUniqueName) {}
|
||||
@Json
|
||||
record CameraNicknameChangeRequest(String name, String cameraUniqueName) {}
|
||||
|
||||
public static void onCameraNicknameChangeRequest(Context ctx) {
|
||||
try {
|
||||
CameraNicknameChangeRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CameraNicknameChangeRequest.class);
|
||||
Jsonb.instance().type(CameraNicknameChangeRequest.class).fromJson(ctx.body());
|
||||
|
||||
VisionSourceManager.getInstance()
|
||||
.vmm
|
||||
@@ -1008,7 +1011,7 @@ public class RequestHandler {
|
||||
ctx.status(200);
|
||||
ctx.result("Successfully changed the camera name to: " + request.name);
|
||||
logger.info("Successfully changed the camera name to: " + request.name);
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(400).result("Invalid JSON format");
|
||||
logger.error("Failed to process camera nickname change request", e);
|
||||
} catch (Exception e) {
|
||||
@@ -1038,8 +1041,8 @@ public class RequestHandler {
|
||||
module.getStateAsCameraConfig().calibrations.stream()
|
||||
.filter(
|
||||
it ->
|
||||
Math.abs(it.unrotatedImageSize.width - width) < 1e-4
|
||||
&& Math.abs(it.unrotatedImageSize.height - height) < 1e-4)
|
||||
Math.abs(it.resolution.width - width) < 1e-4
|
||||
&& Math.abs(it.resolution.height - height) < 1e-4)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@@ -1052,12 +1055,13 @@ public class RequestHandler {
|
||||
ctx.status(200);
|
||||
}
|
||||
|
||||
private record CalibrationRemoveRequest(int width, int height, String cameraUniqueName) {}
|
||||
@Json
|
||||
record CalibrationRemoveRequest(int width, int height, String cameraUniqueName) {}
|
||||
|
||||
public static void onCalibrationRemoveRequest(Context ctx) {
|
||||
try {
|
||||
CalibrationRemoveRequest request =
|
||||
kObjectMapper.readValue(ctx.body(), CalibrationRemoveRequest.class);
|
||||
Jsonb.instance().type(CalibrationRemoveRequest.class).fromJson(ctx.body());
|
||||
|
||||
logger.info(
|
||||
"Attempting to remove calibration for camera: "
|
||||
@@ -1083,7 +1087,7 @@ public class RequestHandler {
|
||||
+ request.width
|
||||
+ "x"
|
||||
+ request.height);
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(400).result("Invalid JSON format");
|
||||
logger.error("Failed to process calibration removed request", e);
|
||||
} catch (Exception e) {
|
||||
@@ -1107,8 +1111,8 @@ public class RequestHandler {
|
||||
.stream()
|
||||
.filter(
|
||||
it ->
|
||||
Math.abs(it.unrotatedImageSize.width - width) < 1e-4
|
||||
&& Math.abs(it.unrotatedImageSize.height - height) < 1e-4)
|
||||
Math.abs(it.resolution.width - width) < 1e-4
|
||||
&& Math.abs(it.resolution.height - height) < 1e-4)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@@ -1146,8 +1150,8 @@ public class RequestHandler {
|
||||
cc.calibrations.stream()
|
||||
.filter(
|
||||
it ->
|
||||
Math.abs(it.unrotatedImageSize.width - width) < 1e-4
|
||||
&& Math.abs(it.unrotatedImageSize.height - height) < 1e-4)
|
||||
Math.abs(it.resolution.width - width) < 1e-4
|
||||
&& Math.abs(it.resolution.height - height) < 1e-4)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@@ -1319,7 +1323,7 @@ public class RequestHandler {
|
||||
public static void onNukeOneCamera(Context ctx) {
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
Jsonb.instance().type(CommonCameraUniqueName.class).fromJson(ctx.body());
|
||||
|
||||
logger.warn("Deleting camera name " + request.cameraUniqueName);
|
||||
|
||||
@@ -1334,7 +1338,7 @@ public class RequestHandler {
|
||||
VisionSourceManager.getInstance().deleteVisionSource(request.cameraUniqueName);
|
||||
|
||||
ctx.status(200);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | IllegalStateException | JsonException e) {
|
||||
logger.error("Failed to delete camera", e);
|
||||
ctx.status(500);
|
||||
ctx.result("Failed to delete camera");
|
||||
@@ -1346,7 +1350,7 @@ public class RequestHandler {
|
||||
logger.info(ctx.queryString());
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
Jsonb.instance().type(CommonCameraUniqueName.class).fromJson(ctx.body());
|
||||
|
||||
if (VisionSourceManager.getInstance()
|
||||
.reactivateDisabledCameraConfig(request.cameraUniqueName)) {
|
||||
@@ -1354,7 +1358,7 @@ public class RequestHandler {
|
||||
} else {
|
||||
ctx.status(403);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process activate matched camera request", e);
|
||||
ctx.result("Failed to process activate matched camera request");
|
||||
@@ -1362,14 +1366,15 @@ public class RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private record AssignUnmatchedCamera(PVCameraInfo cameraInfo) {}
|
||||
@Json
|
||||
record AssignUnmatchedCamera(PVCameraInfo cameraInfo) {}
|
||||
|
||||
public static void onAssignUnmatchedCameraRequest(Context ctx) {
|
||||
logger.info(ctx.queryString());
|
||||
|
||||
try {
|
||||
AssignUnmatchedCamera request =
|
||||
kObjectMapper.readValue(ctx.body(), AssignUnmatchedCamera.class);
|
||||
Jsonb.instance().type(AssignUnmatchedCamera.class).fromJson(ctx.body());
|
||||
|
||||
if (request.cameraInfo == null) {
|
||||
ctx.status(400);
|
||||
@@ -1385,7 +1390,7 @@ public class RequestHandler {
|
||||
}
|
||||
|
||||
ctx.result("Successfully assigned camera: " + request.cameraInfo);
|
||||
} catch (IOException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process assign unmatched camera request", e);
|
||||
ctx.result("Failed to process assign unmatched camera request");
|
||||
@@ -1397,14 +1402,14 @@ public class RequestHandler {
|
||||
logger.info(ctx.queryString());
|
||||
try {
|
||||
CommonCameraUniqueName request =
|
||||
kObjectMapper.readValue(ctx.body(), CommonCameraUniqueName.class);
|
||||
Jsonb.instance().type(CommonCameraUniqueName.class).fromJson(ctx.body());
|
||||
|
||||
if (VisionSourceManager.getInstance().deactivateVisionSource(request.cameraUniqueName)) {
|
||||
ctx.status(200);
|
||||
} else {
|
||||
ctx.status(403);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IllegalStateException | JsonException e) {
|
||||
ctx.status(401);
|
||||
logger.error("Failed to process unassign camera request", e);
|
||||
ctx.result("Failed to process unassign camera request");
|
||||
|
||||
@@ -41,7 +41,7 @@ public class Server {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChangeEvent(DataChangeEvent<?> event) {
|
||||
public <T> void onDataChangeEvent(DataChangeEvent<T> event) {
|
||||
if (event.propertyName.equals("restartServer")) {
|
||||
Server.restart();
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
|
||||
package org.photonvision.server;
|
||||
|
||||
import io.avaje.jsonb.Json;
|
||||
import io.avaje.jsonb.Jsonb;
|
||||
import io.javalin.http.Context;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.configuration.NeuralNetworkModelManager;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.file.JacksonUtils;
|
||||
|
||||
public class TestRequestHandler {
|
||||
// Treat all 2XX calls as "INFO"
|
||||
@@ -39,12 +40,13 @@ public class TestRequestHandler {
|
||||
ConfigManager.getInstance().load();
|
||||
}
|
||||
|
||||
private record PlatformOverrideRequest(Platform platform) {}
|
||||
@Json
|
||||
record PlatformOverrideRequest(Platform platform) {}
|
||||
|
||||
public static void handlePlatformOverrideRequest(Context ctx) {
|
||||
try {
|
||||
PlatformOverrideRequest request =
|
||||
JacksonUtils.deserialize(ctx.body(), PlatformOverrideRequest.class);
|
||||
Jsonb.instance().type(PlatformOverrideRequest.class).fromJson(ctx.body());
|
||||
Platform platform = request.platform();
|
||||
logger.info("Overriding platform to: " + platform);
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ public class UIInboundSubscriber extends DataChangeSubscriber {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChangeEvent(DataChangeEvent<?> event) {
|
||||
if (event instanceof IncomingWebSocketEvent incomingWSEvent) {
|
||||
public <T> void onDataChangeEvent(DataChangeEvent<T> event) {
|
||||
if (event instanceof IncomingWebSocketEvent<T> incomingWSEvent) {
|
||||
if (incomingWSEvent.propertyName.equals("userConnected")
|
||||
|| incomingWSEvent.propertyName.equals("sendFullSettings")) {
|
||||
// Send full settings
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.photonvision.server;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.avaje.json.JsonDataException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.DataChangeDestination;
|
||||
@@ -43,15 +43,15 @@ class UIOutboundSubscriber extends DataChangeSubscriber {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChangeEvent(DataChangeEvent event) {
|
||||
if (event instanceof OutgoingUIEvent thisEvent) {
|
||||
public <T> void onDataChangeEvent(DataChangeEvent<T> event) {
|
||||
if (event instanceof OutgoingUIEvent<T> thisEvent) {
|
||||
try {
|
||||
if (event.data instanceof HashMap data) {
|
||||
socketHandler.broadcastMessage(data, thisEvent.originContext);
|
||||
} else {
|
||||
socketHandler.broadcastMessage(event.data, thisEvent.originContext);
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
} catch (JsonDataException e) {
|
||||
logger.error("Failed to process outgoing message!", e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user