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:
Alan Everett
2026-05-24 13:05:10 -04:00
committed by GitHub
parent 4db3d7be57
commit 0525e762b4
95 changed files with 1306 additions and 1216 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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"),

View File

@@ -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");

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}
}