diff --git a/photon-lib/py/photonlibpy/photonCamera.py b/photon-lib/py/photonlibpy/photonCamera.py index 1ccca6d44..41588e7bd 100644 --- a/photon-lib/py/photonlibpy/photonCamera.py +++ b/photon-lib/py/photonlibpy/photonCamera.py @@ -146,6 +146,16 @@ class PhotonCamera: cameraNames = ( self._cameraTable.getInstance().getTable(self._tableName).getSubTables() ) + # Look for only cameras with rawBytes entry that exists + cameraNames = list( + filter( + lambda it: self._cameraTable.getSubTable(it) + .getEntry("rawBytes") + .exists(), + cameraNames, + ) + ) + if len(cameraNames) == 0: wpilib.reportError( "Could not find any PhotonVision coprocessors on NetworkTables. Double check that PhotonVision is running, and that your camera is connected!", diff --git a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java index 6e06bc1b6..54173a41b 100644 --- a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java +++ b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java @@ -46,8 +46,9 @@ import edu.wpi.first.networktables.StringSubscriber; import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.RobotController; import edu.wpi.first.wpilibj.Timer; +import java.util.List; import java.util.Optional; -import java.util.Set; +import java.util.stream.Collectors; import org.photonvision.common.hardware.VisionLEDMode; import org.photonvision.common.networktables.PacketSubscriber; import org.photonvision.targeting.PhotonPipelineResult; @@ -75,6 +76,8 @@ public class PhotonCamera implements AutoCloseable { IntegerSubscriber heartbeatEntry; DoubleArraySubscriber cameraIntrinsicsSubscriber; DoubleArraySubscriber cameraDistortionSubscriber; + MultiSubscriber topicNameSubscriber; + NetworkTable rootPhotonTable; @Override public void close() { @@ -98,6 +101,7 @@ public class PhotonCamera implements AutoCloseable { pipelineIndexRequest.close(); cameraIntrinsicsSubscriber.close(); cameraDistortionSubscriber.close(); + topicNameSubscriber.close(); } private final String path; @@ -125,8 +129,8 @@ public class PhotonCamera implements AutoCloseable { */ public PhotonCamera(NetworkTableInstance instance, String cameraName) { name = cameraName; - var photonvision_root_table = instance.getTable(kTableName); - this.cameraTable = photonvision_root_table.getSubTable(cameraName); + rootPhotonTable = instance.getTable(kTableName); + this.cameraTable = rootPhotonTable.getSubTable(cameraName); path = cameraTable.getPath(); var rawBytesEntry = cameraTable @@ -148,12 +152,12 @@ public class PhotonCamera implements AutoCloseable { cameraDistortionSubscriber = cameraTable.getDoubleArrayTopic("cameraDistortion").subscribe(null); - ledModeRequest = photonvision_root_table.getIntegerTopic("ledModeRequest").publish(); - ledModeState = photonvision_root_table.getIntegerTopic("ledModeState").subscribe(-1); - versionEntry = photonvision_root_table.getStringTopic("version").subscribe(""); + ledModeRequest = rootPhotonTable.getIntegerTopic("ledModeRequest").publish(); + ledModeState = rootPhotonTable.getIntegerTopic("ledModeState").subscribe(-1); + versionEntry = rootPhotonTable.getStringTopic("version").subscribe(""); // Existing is enough to make this multisubscriber do its thing - MultiSubscriber m_topicNameSubscriber = + topicNameSubscriber = new MultiSubscriber( instance, new String[] {"/photonvision/"}, PubSubOption.topicsOnly(true)); @@ -333,10 +337,10 @@ public class PhotonCamera implements AutoCloseable { // Heartbeat entry is assumed to always be present. If it's not present, we // assume that a camera with that name was never connected in the first place. if (!heartbeatEntry.exists()) { - Set cameraNames = cameraTable.getInstance().getTable(kTableName).getSubTables(); + var cameraNames = getTablesThatLookLikePhotonCameras(); if (cameraNames.isEmpty()) { DriverStation.reportError( - "Could not find any PhotonVision coprocessors on NetworkTables. Double check that PhotonVision is running, and that your camera is connected!", + "Could not find **any** PhotonVision coprocessors on NetworkTables. Double check that PhotonVision is running, and that your camera is connected!", false); } else { DriverStation.reportError( @@ -344,9 +348,17 @@ public class PhotonCamera implements AutoCloseable { + path + " not found on NetworkTables. Double check that your camera names match!", true); + + var cameraNameStr = new StringBuilder(); + for (var c : cameraNames) { + cameraNameStr.append(" ==> "); + cameraNameStr.append(c); + cameraNameStr.append("\n"); + } + DriverStation.reportError( "Found the following PhotonVision cameras on NetworkTables:\n" - + String.join("\n", cameraNames), + + cameraNameStr.toString(), false); } } @@ -391,4 +403,13 @@ public class PhotonCamera implements AutoCloseable { throw new UnsupportedOperationException(versionMismatchMessage); } } + + private List getTablesThatLookLikePhotonCameras() { + return rootPhotonTable.getSubTables().stream() + .filter( + it -> { + return rootPhotonTable.getSubTable(it).getEntry("rawBytes").exists(); + }) + .collect(Collectors.toList()); + } } diff --git a/photon-lib/src/main/native/cpp/photon/PhotonCamera.cpp b/photon-lib/src/main/native/cpp/photon/PhotonCamera.cpp index 94225d175..47be45075 100644 --- a/photon-lib/src/main/native/cpp/photon/PhotonCamera.cpp +++ b/photon-lib/src/main/native/cpp/photon/PhotonCamera.cpp @@ -91,9 +91,9 @@ PhotonCamera::PhotonCamera(nt::NetworkTableInstance instance, rootTable->GetBooleanTopic("driverMode").Subscribe(false)), driverModePublisher( rootTable->GetBooleanTopic("driverModeRequest").Publish()), - m_topicNameSubscriber(instance, PHOTON_PREFIX, {.topicsOnly = true}), + topicNameSubscriber(instance, PHOTON_PREFIX, {.topicsOnly = true}), path(rootTable->GetPath()), - m_cameraName(cameraName) { + cameraName(cameraName) { HAL_Report(HALUsageReporting::kResourceType_PhotonCamera, InstanceCount); InstanceCount++; } @@ -173,7 +173,7 @@ void PhotonCamera::SetLEDMode(LEDMode mode) { } const std::string_view PhotonCamera::GetCameraName() const { - return m_cameraName; + return cameraName; } std::optional PhotonCamera::GetDistCoeffs() { @@ -232,4 +232,17 @@ void PhotonCamera::VerifyVersion() { } } +std::vector PhotonCamera::tablesThatLookLikePhotonCameras() { + std::vector cameraNames = mainTable->GetSubTables(); + + std::vector ret; + std::copy_if( + cameraNames.begin(), cameraNames.end(), std::back_inserter(ret), + [this](auto& it) { + return mainTable->GetSubTable(it)->GetEntry("rawBytes").Exists(); + }); + + return ret; +} + } // namespace photon diff --git a/photon-lib/src/main/native/include/photon/PhotonCamera.h b/photon-lib/src/main/native/include/photon/PhotonCamera.h index d9cf2574e..6d29754bf 100644 --- a/photon-lib/src/main/native/include/photon/PhotonCamera.h +++ b/photon-lib/src/main/native/include/photon/PhotonCamera.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -182,10 +183,10 @@ class PhotonCamera { nt::BooleanPublisher driverModePublisher; nt::IntegerSubscriber ledModeSubscriber; - nt::MultiSubscriber m_topicNameSubscriber; + nt::MultiSubscriber topicNameSubscriber; std::string path; - std::string m_cameraName; + std::string cameraName; mutable Packet packet; @@ -195,6 +196,8 @@ class PhotonCamera { inline static int InstanceCount = 0; void VerifyVersion(); + + std::vector tablesThatLookLikePhotonCameras(); }; } // namespace photon diff --git a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java index 0ea9f05d7..1e8cf51fb 100644 --- a/photon-server/src/main/java/org/photonvision/server/RequestHandler.java +++ b/photon-server/src/main/java/org/photonvision/server/RequestHandler.java @@ -349,12 +349,12 @@ public class RequestHandler { public static void onGeneralSettingsRequest(Context ctx) { NetworkConfig config; try { - config = kObjectMapper.readValue(ctx.body(), NetworkConfig.class); + config = kObjectMapper.readValue(ctx.bodyInputStream(), NetworkConfig.class); ctx.status(200); ctx.result("Successfully saved general settings"); logger.info("Successfully saved general settings"); - } catch (JsonProcessingException e) { + } catch (IOException e) { // If the settings can't be parsed, use the default network settings config = new NetworkConfig(); @@ -381,7 +381,7 @@ public class RequestHandler { public static void onCameraSettingsRequest(Context ctx) { try { - var data = kObjectMapper.readTree(ctx.body()); + var data = kObjectMapper.readTree(ctx.bodyInputStream()); int index = data.get("index").asInt(); var settings = @@ -451,7 +451,7 @@ public class RequestHandler { int index; try { - index = kObjectMapper.readTree(ctx.body()).get("index").asInt(); + index = kObjectMapper.readTree(ctx.bodyInputStream()).get("index").asInt(); var calData = VisionModuleManager.getInstance().getModule(index).endCalibration(); if (calData == null) { @@ -482,7 +482,7 @@ public class RequestHandler { } public static void onCalibDBCalibrationImportRequest(Context ctx) { - var data = ctx.body(); + var data = ctx.bodyInputStream(); try { var actualObj = kObjectMapper.readTree(data); @@ -503,7 +503,7 @@ public class RequestHandler { ctx.status(200); ctx.result("Calibration imported successfully from CalibDB data!"); logger.info("Calibration imported successfully from CalibDB data!"); - } catch (JsonProcessingException e) { + } catch (IOException e) { ctx.status(400); ctx.result( "The Provided CalibDB data is malformed and cannot be parsed for the required fields."); @@ -515,7 +515,7 @@ public class RequestHandler { public static void onDataCalibrationImportRequest(Context ctx) { try { - var data = kObjectMapper.readTree(ctx.body()); + var data = kObjectMapper.readTree(ctx.bodyInputStream()); int cameraIndex = data.get("cameraIndex").asInt(); var coeffs = @@ -557,7 +557,7 @@ public class RequestHandler { public static void onCameraNicknameChangeRequest(Context ctx) { try { - var data = kObjectMapper.readTree(ctx.body()); + var data = kObjectMapper.readTree(ctx.bodyInputStream()); String name = data.get("name").asText(); int idx = data.get("cameraIndex").asInt(); diff --git a/photon-server/src/main/java/org/photonvision/server/Server.java b/photon-server/src/main/java/org/photonvision/server/Server.java index 0fc0d97f0..143e099c9 100644 --- a/photon-server/src/main/java/org/photonvision/server/Server.java +++ b/photon-server/src/main/java/org/photonvision/server/Server.java @@ -65,10 +65,6 @@ public class Server { 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 = @@ -77,6 +73,12 @@ public class Server { .add(ctx.req().getMethod()) .add("from endpoint") .add(ctx.path()) + .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())) .add("for host") .add(ctx.req().getRemoteHost()) .add("in")