2020-07-02 22:02:21 -04:00
|
|
|
/*
|
2020-12-31 19:57:51 -08:00
|
|
|
* Copyright (C) Photon Vision.
|
2020-07-02 22:02:21 -04:00
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
2022-01-20 19:35:28 -08:00
|
|
|
|
2020-06-27 14:58:03 -07:00
|
|
|
package org.photonvision.server;
|
|
|
|
|
|
|
|
|
|
import io.javalin.Javalin;
|
2025-10-13 20:58:14 -05:00
|
|
|
import io.javalin.plugin.bundled.CorsPlugin;
|
2023-10-05 18:22:56 -04:00
|
|
|
import java.net.InetSocketAddress;
|
2024-01-08 13:02:31 -05:00
|
|
|
import java.util.List;
|
2023-06-25 21:07:27 -04:00
|
|
|
import java.util.StringJoiner;
|
2024-01-08 13:02:31 -05:00
|
|
|
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;
|
Bootup sprint (#18)
* Did some stuff
* Fix gradle, start implementing mjpeg frame consumer
* Did some stuff
* bade changes
* rename camera config to USBCameraConfiguration, add name
* unrename cameraconfiguration
* Add pub/sub framework
* Add setResolution to mjpeg frame consumer
* add NTDataConsumer
* Add some totally broken hsv hacks
* Start refactoring UI data
* Update index.js
* Commit and push, he says
* Fix up some errors
* Fix input tab
* Fix fps
* Update index.js
* Add pipeline field setting, update PipelineManager, fix nullpointers and USBCameraSettables
* Change v-model to point to data()
* update hsv to use mutations
* Work on saving, fix hsv
* Rename shouldErode/shouldDilate to erode and dilate
* Hook all the tabs up to the Store
* Change handleData to handlePipelineData
* camera quirk redo, add ICCSub to SocketHandler
* Fix some property names
* Fixed tons of naming in UI, fix backend for multi-val PSCs, fix PSC enums
* change pipeline type to an int in store
* Fix mutation naming
* Attempt threshold fix
* Update SocketHandler.java
* Add truthy data sending
* Start adding logging support
* [UI] Add delay to slider input boxes (#1)
* [UI] [Backend] potentially fix camera settings, various logging tweaks
* Don't release raw input mat
* add setVideoModeIndex to vision settables
* Implement pipeline index in socket handler, add framework for renaming/changing pipes
* (ish) get pipeline change working
* Create index.html
* Cleanups, fix pipeline index bug, fix stream res for MJPG, add dashboard stream (unused)
* Refactor UI to use mutatePipeline, send pipeline results
* Update NetworkConfig.java
* Change double to number
* Run spotless
* Fix reversal of large/small comparators
* Fix left/right
* Fix pitch/yaw calculation bug, fix area bug
* Use Vue.set instead of assignment
This fixes {{ }}
* Update App.vue
* run spotless
* Actually add pipelines and reassign indecies
* Delete old pipeline configs
Fixes duplication on renaming pipeline
* Start working on deleting pipes
* Fix camera nickname change
* run spotless
* Fix some test stuff
* Update VisionModuleManagerTest.java
* vision source manager test is still broken
* Fix VisionSourceManager test
* Apply spotless 2 electric boogaloo
Co-authored-by: Banks Troutman <btrout.dhrs@gmail.com>
Co-authored-by: Declan Freeman-Gleason <declanfreemangleason@gmail.com>
Co-authored-by: Aaryan Agrawal <54345060+13Ducks@users.noreply.github.com>
2020-07-07 01:01:58 -07:00
|
|
|
import org.photonvision.common.logging.LogGroup;
|
|
|
|
|
import org.photonvision.common.logging.Logger;
|
2020-06-27 14:58:03 -07:00
|
|
|
|
|
|
|
|
public class Server {
|
2020-07-11 22:43:19 -04:00
|
|
|
private static final Logger logger = new Logger(Server.class, LogGroup.WebServer);
|
2020-06-27 14:58:03 -07:00
|
|
|
|
2024-01-08 13:02:31 -05:00
|
|
|
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 =
|
2020-06-28 04:40:43 -04:00
|
|
|
Javalin.create(
|
2023-10-05 18:22:56 -04:00
|
|
|
javalinConfig -> {
|
|
|
|
|
javalinConfig.showJavalinBanner = false;
|
|
|
|
|
javalinConfig.staticFiles.add("web");
|
2025-10-13 20:58:14 -05:00
|
|
|
javalinConfig.registerPlugin(
|
|
|
|
|
new CorsPlugin(
|
|
|
|
|
cors -> {
|
|
|
|
|
cors.addRule(
|
|
|
|
|
it -> {
|
|
|
|
|
it.anyHost();
|
|
|
|
|
});
|
|
|
|
|
}));
|
2023-10-05 18:22:56 -04:00
|
|
|
javalinConfig.requestLogger.http(
|
2023-06-25 21:07:27 -04:00
|
|
|
(ctx, ms) -> {
|
|
|
|
|
StringJoiner joiner =
|
|
|
|
|
new StringJoiner(" ")
|
|
|
|
|
.add("Handled HTTP request of type")
|
2023-10-05 18:22:56 -04:00
|
|
|
.add(ctx.req().getMethod())
|
2023-06-25 21:07:27 -04:00
|
|
|
.add("from endpoint")
|
|
|
|
|
.add(ctx.path())
|
2024-05-10 14:58:18 -04:00
|
|
|
.add("of req size")
|
|
|
|
|
.add(Integer.toString(ctx.contentLength()))
|
|
|
|
|
.add("bytes & type")
|
|
|
|
|
.add(ctx.contentType())
|
|
|
|
|
.add("with return code")
|
|
|
|
|
.add(Integer.toString(ctx.res().getStatus()))
|
2023-06-25 21:07:27 -04:00
|
|
|
.add("for host")
|
2023-10-05 18:22:56 -04:00
|
|
|
.add(ctx.req().getRemoteHost())
|
2023-06-25 21:07:27 -04:00
|
|
|
.add("in")
|
|
|
|
|
.add(ms.toString())
|
|
|
|
|
.add("ms");
|
|
|
|
|
|
|
|
|
|
logger.debug(joiner.toString());
|
|
|
|
|
});
|
2023-10-05 18:22:56 -04:00
|
|
|
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;
|
|
|
|
|
}));
|
|
|
|
|
});
|
2020-06-28 04:40:43 -04:00
|
|
|
});
|
Bootup sprint (#18)
* Did some stuff
* Fix gradle, start implementing mjpeg frame consumer
* Did some stuff
* bade changes
* rename camera config to USBCameraConfiguration, add name
* unrename cameraconfiguration
* Add pub/sub framework
* Add setResolution to mjpeg frame consumer
* add NTDataConsumer
* Add some totally broken hsv hacks
* Start refactoring UI data
* Update index.js
* Commit and push, he says
* Fix up some errors
* Fix input tab
* Fix fps
* Update index.js
* Add pipeline field setting, update PipelineManager, fix nullpointers and USBCameraSettables
* Change v-model to point to data()
* update hsv to use mutations
* Work on saving, fix hsv
* Rename shouldErode/shouldDilate to erode and dilate
* Hook all the tabs up to the Store
* Change handleData to handlePipelineData
* camera quirk redo, add ICCSub to SocketHandler
* Fix some property names
* Fixed tons of naming in UI, fix backend for multi-val PSCs, fix PSC enums
* change pipeline type to an int in store
* Fix mutation naming
* Attempt threshold fix
* Update SocketHandler.java
* Add truthy data sending
* Start adding logging support
* [UI] Add delay to slider input boxes (#1)
* [UI] [Backend] potentially fix camera settings, various logging tweaks
* Don't release raw input mat
* add setVideoModeIndex to vision settables
* Implement pipeline index in socket handler, add framework for renaming/changing pipes
* (ish) get pipeline change working
* Create index.html
* Cleanups, fix pipeline index bug, fix stream res for MJPG, add dashboard stream (unused)
* Refactor UI to use mutatePipeline, send pipeline results
* Update NetworkConfig.java
* Change double to number
* Run spotless
* Fix reversal of large/small comparators
* Fix left/right
* Fix pitch/yaw calculation bug, fix area bug
* Use Vue.set instead of assignment
This fixes {{ }}
* Update App.vue
* run spotless
* Actually add pipelines and reassign indecies
* Delete old pipeline configs
Fixes duplication on renaming pipeline
* Start working on deleting pipes
* Fix camera nickname change
* run spotless
* Fix some test stuff
* Update VisionModuleManagerTest.java
* vision source manager test is still broken
* Fix VisionSourceManager test
* Apply spotless 2 electric boogaloo
Co-authored-by: Banks Troutman <btrout.dhrs@gmail.com>
Co-authored-by: Declan Freeman-Gleason <declanfreemangleason@gmail.com>
Co-authored-by: Aaryan Agrawal <54345060+13Ducks@users.noreply.github.com>
2020-07-07 01:01:58 -07:00
|
|
|
|
2025-01-12 19:48:39 -06:00
|
|
|
/* Web Socket Events for Data Exchange */
|
2022-10-30 13:16:17 -05:00
|
|
|
var dsHandler = DataSocketHandler.getInstance();
|
|
|
|
|
app.ws(
|
|
|
|
|
"/websocket_data",
|
|
|
|
|
ws -> {
|
|
|
|
|
ws.onConnect(dsHandler::onConnect);
|
|
|
|
|
ws.onClose(dsHandler::onClose);
|
2025-07-09 00:40:21 -04:00
|
|
|
ws.onError(e -> logger.error(e.toString(), e.error()));
|
2022-10-30 13:16:17 -05:00
|
|
|
ws.onBinaryMessage(dsHandler::onBinaryMessage);
|
|
|
|
|
});
|
2023-06-25 21:07:27 -04:00
|
|
|
|
2025-01-12 19:48:39 -06:00
|
|
|
/* API Events */
|
Force reload after restart and switch URL after IP change (#2278)
## Description
Forces a reload after restarting PhotonVision, restarting the
coprocessor, performing an offline update, or nuking the install. We
wait until we are reconnected to the coprocessor to reload, this is
accomplished by the addition of a status API endpoint.
This is being implemented due to issues experienced when the webpage is
not updated (particularly during offline updates).
---
Using the same statusCheck, we also wait until a new IP is available,
then change to it, after changing our static IP.
---
closes #2169
closes #903
## 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_
- [ ] 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 settings back to v2025.3.2
- [ ] 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
2026-01-07 01:53:05 -06:00
|
|
|
app.get("/api/status", RequestHandler::onStatusRequest);
|
|
|
|
|
|
2023-06-25 21:07:27 -04:00
|
|
|
// 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);
|
2023-10-17 10:20:00 -04:00
|
|
|
app.post("/api/settings/aprilTagFieldLayout", RequestHandler::onAprilTagFieldLayoutRequest);
|
2023-06-25 21:07:27 -04:00
|
|
|
app.post("/api/settings/general", RequestHandler::onGeneralSettingsRequest);
|
|
|
|
|
app.post("/api/settings/camera", RequestHandler::onCameraSettingsRequest);
|
|
|
|
|
app.post("/api/settings/camera/setNickname", RequestHandler::onCameraNicknameChangeRequest);
|
2024-01-03 14:32:04 -07:00
|
|
|
app.get("/api/settings/camera/getCalibImages", RequestHandler::onCameraCalibImagesRequest);
|
2025-12-26 21:20:36 -05:00
|
|
|
app.get("/api/settings/camera/getCalibration", RequestHandler::onCalibrationJsonRequest);
|
2023-06-25 21:07:27 -04:00
|
|
|
|
|
|
|
|
// Utilities
|
|
|
|
|
app.post("/api/utils/offlineUpdate", RequestHandler::onOfflineUpdateRequest);
|
2023-10-20 08:04:19 -04:00
|
|
|
app.get("/api/utils/photonvision-journalctl.txt", RequestHandler::onLogExportRequest);
|
2023-06-25 21:07:27 -04:00
|
|
|
app.post("/api/utils/restartProgram", RequestHandler::onProgramRestartRequest);
|
|
|
|
|
app.post("/api/utils/restartDevice", RequestHandler::onDeviceRestartRequest);
|
2023-11-05 11:33:45 -05:00
|
|
|
app.get("/api/utils/getImageSnapshots", RequestHandler::onImageSnapshotsRequest);
|
2024-02-01 21:42:54 -05:00
|
|
|
app.get("/api/utils/getCalSnapshot", RequestHandler::onCalibrationSnapshotRequest);
|
|
|
|
|
app.get("/api/utils/getCalibrationJSON", RequestHandler::onCalibrationExportRequest);
|
2024-10-24 20:48:02 -07:00
|
|
|
app.post("/api/utils/nukeConfigDirectory", RequestHandler::onNukeConfigDirectory);
|
|
|
|
|
app.post("/api/utils/nukeOneCamera", RequestHandler::onNukeOneCamera);
|
2025-01-01 03:04:20 -05:00
|
|
|
app.post("/api/utils/activateMatchedCamera", RequestHandler::onActivateMatchedCameraRequest);
|
|
|
|
|
app.post("/api/utils/assignUnmatchedCamera", RequestHandler::onAssignUnmatchedCameraRequest);
|
|
|
|
|
app.post("/api/utils/unassignCamera", RequestHandler::onUnassignCameraRequest);
|
2023-06-25 21:07:27 -04:00
|
|
|
|
|
|
|
|
// Calibration
|
|
|
|
|
app.post("/api/calibration/end", RequestHandler::onCalibrationEndRequest);
|
2024-01-03 14:32:04 -07:00
|
|
|
app.post("/api/calibration/importFromData", RequestHandler::onDataCalibrationImportRequest);
|
2025-11-02 15:17:22 -06:00
|
|
|
app.post("/api/calibration/remove", RequestHandler::onCalibrationRemoveRequest);
|
2020-08-14 12:39:21 -07:00
|
|
|
|
2025-06-30 22:02:44 -05:00
|
|
|
// Object detection
|
|
|
|
|
app.post("/api/objectdetection/import", RequestHandler::onImportObjectDetectionModelRequest);
|
|
|
|
|
app.post(
|
|
|
|
|
"/api/objectdetection/bulkimport", RequestHandler::onBulkImportObjectDetectionModelRequest);
|
|
|
|
|
app.get("/api/objectdetection/export", RequestHandler::onExportObjectDetectionModelsRequest);
|
|
|
|
|
app.get(
|
|
|
|
|
"/api/objectdetection/exportIndividual",
|
|
|
|
|
RequestHandler::onExportIndividualObjectDetectionModelRequest);
|
|
|
|
|
app.post("/api/objectdetection/delete", RequestHandler::onDeleteObjectDetectionModelRequest);
|
|
|
|
|
app.post("/api/objectdetection/rename", RequestHandler::onRenameObjectDetectionModelRequest);
|
|
|
|
|
app.post("/api/objectdetection/nuke", RequestHandler::onNukeObjectDetectionModelsRequest);
|
|
|
|
|
|
2025-12-04 22:25:48 -06:00
|
|
|
/* Testing API Events */
|
|
|
|
|
|
|
|
|
|
app.post("/api/test/resetBackend", TestRequestHandler::handleResetRequest);
|
|
|
|
|
|
|
|
|
|
app.post("/api/test/activateTestMode", TestRequestHandler::testMode);
|
|
|
|
|
app.post("/api/test/override/platform", TestRequestHandler::handlePlatformOverrideRequest);
|
|
|
|
|
|
2020-06-28 04:40:43 -04:00
|
|
|
app.start(port);
|
2024-01-08 13:02:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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);
|
2020-06-28 04:40:43 -04:00
|
|
|
}
|
2020-06-27 14:58:03 -07:00
|
|
|
}
|