Files
PhotonVision/photon-server/src/main/java/org/photonvision/server/Server.java
Matt 62112cd2fd Reduce initial connection bandwidth (#1200)
Reduces bandwidth requirements by being much lazier about how much calibration data is sent to the UI.
2024-02-01 21:42:54 -05:00

156 lines
7.6 KiB
Java

/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.server;
import io.javalin.Javalin;
import io.javalin.plugin.bundled.CorsPluginConfig;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.StringJoiner;
import org.photonvision.common.dataflow.DataChangeDestination;
import org.photonvision.common.dataflow.DataChangeService;
import org.photonvision.common.dataflow.DataChangeSource;
import org.photonvision.common.dataflow.DataChangeSubscriber;
import org.photonvision.common.dataflow.events.DataChangeEvent;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
public class Server {
private static final Logger logger = new Logger(Server.class, LogGroup.WebServer);
private static Javalin app = null;
static class RestartSubscriber extends DataChangeSubscriber {
private RestartSubscriber() {
super(DataChangeSource.AllSources, List.of(DataChangeDestination.DCD_WEBSERVER));
}
@Override
public void onDataChangeEvent(DataChangeEvent<?> event) {
if (event.propertyName.equals("restartServer")) {
Server.restart();
}
}
}
public static void initialize(int port) {
DataChangeService.getInstance().addSubscriber(new RestartSubscriber());
start(port);
}
private static void start(int port) {
app =
Javalin.create(
javalinConfig -> {
javalinConfig.showJavalinBanner = false;
javalinConfig.staticFiles.add("web");
javalinConfig.plugins.enableCors(
corsContainer -> {
corsContainer.add(CorsPluginConfig::anyHost);
});
// Increase the upload size limit (arbitrary, but need to be able to deal with large
// calibration JSONs)
javalinConfig.http.maxRequestSize = (long) (50 * 1e6);
javalinConfig.requestLogger.http(
(ctx, ms) -> {
StringJoiner joiner =
new StringJoiner(" ")
.add("Handled HTTP request of type")
.add(ctx.req().getMethod())
.add("from endpoint")
.add(ctx.path())
.add("for host")
.add(ctx.req().getRemoteHost())
.add("in")
.add(ms.toString())
.add("ms");
logger.debug(joiner.toString());
});
javalinConfig.requestLogger.ws(
ws -> {
ws.onMessage(ctx -> logger.debug("Got WebSockets message: " + ctx.message()));
ws.onBinaryMessage(
ctx ->
logger.trace(
() -> {
var remote = (InetSocketAddress) ctx.session.getRemoteAddress();
var host =
remote.getAddress().toString() + ":" + remote.getPort();
return "Got WebSockets binary message from host: " + host;
}));
});
});
/*Web Socket Events for Data Exchange */
var dsHandler = DataSocketHandler.getInstance();
app.ws(
"/websocket_data",
ws -> {
ws.onConnect(dsHandler::onConnect);
ws.onClose(dsHandler::onClose);
ws.onBinaryMessage(dsHandler::onBinaryMessage);
});
/*API Events*/
// Settings
app.post("/api/settings", RequestHandler::onSettingsImportRequest);
app.get("/api/settings/photonvision_config.zip", RequestHandler::onSettingsExportRequest);
app.post("/api/settings/hardwareConfig", RequestHandler::onHardwareConfigRequest);
app.post("/api/settings/hardwareSettings", RequestHandler::onHardwareSettingsRequest);
app.post("/api/settings/networkConfig", RequestHandler::onNetworkConfigRequest);
app.post("/api/settings/aprilTagFieldLayout", RequestHandler::onAprilTagFieldLayoutRequest);
app.post("/api/settings/general", RequestHandler::onGeneralSettingsRequest);
app.post("/api/settings/camera", RequestHandler::onCameraSettingsRequest);
app.post("/api/settings/camera/setNickname", RequestHandler::onCameraNicknameChangeRequest);
app.get("/api/settings/camera/getCalibImages", RequestHandler::onCameraCalibImagesRequest);
// Utilities
app.post("/api/utils/offlineUpdate", RequestHandler::onOfflineUpdateRequest);
app.get("/api/utils/photonvision-journalctl.txt", RequestHandler::onLogExportRequest);
app.post("/api/utils/restartProgram", RequestHandler::onProgramRestartRequest);
app.post("/api/utils/restartDevice", RequestHandler::onDeviceRestartRequest);
app.post("/api/utils/publishMetrics", RequestHandler::onMetricsPublishRequest);
app.get("/api/utils/getImageSnapshots", RequestHandler::onImageSnapshotsRequest);
app.get("/api/utils/getCalSnapshot", RequestHandler::onCalibrationSnapshotRequest);
app.get("/api/utils/getCalibrationJSON", RequestHandler::onCalibrationExportRequest);
// Calibration
app.post("/api/calibration/end", RequestHandler::onCalibrationEndRequest);
app.post(
"/api/calibration/importFromCalibDB", RequestHandler::onCalibDBCalibrationImportRequest);
app.post("/api/calibration/importFromData", RequestHandler::onDataCalibrationImportRequest);
app.start(port);
}
/**
* Seems like if we change the static IP of this device, Javalin refuses to tell us when new
* Websocket clients connect. As a hack, we can restart the server every time we change static IPs
*/
public static void restart() {
logger.info("Web server going down for restart");
int oldPort = app.port();
app.stop();
start(oldPort);
}
}