Allow file uploads of any size and better report active cameras in PhotonCamera error print (#1298)

Previously reported itself which was confusing. New print:

```
Error at org.photonvision.PhotonCamera.verifyVersion(PhotonCamera.java:378): Found the following PhotonVision cameras active on NetworkTables:
 ==> HD_Pro_Webcam_C920
 ==> Arducam_OV9281_USB_Camera
```
This commit is contained in:
Matt
2024-05-10 14:58:18 -04:00
committed by GitHub
parent 1708376df8
commit 713fad6f6b
6 changed files with 76 additions and 27 deletions

View File

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

View File

@@ -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<String> 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<String> getTablesThatLookLikePhotonCameras() {
return rootPhotonTable.getSubTables().stream()
.filter(
it -> {
return rootPhotonTable.getSubTable(it).getEntry("rawBytes").exists();
})
.collect(Collectors.toList());
}
}

View File

@@ -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<cv::Mat> PhotonCamera::GetDistCoeffs() {
@@ -232,4 +232,17 @@ void PhotonCamera::VerifyVersion() {
}
}
std::vector<std::string> PhotonCamera::tablesThatLookLikePhotonCameras() {
std::vector<std::string> cameraNames = mainTable->GetSubTables();
std::vector<std::string> 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

View File

@@ -26,6 +26,7 @@
#include <memory>
#include <string>
#include <vector>
#include <networktables/BooleanTopic.h>
#include <networktables/DoubleArrayTopic.h>
@@ -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<std::string> tablesThatLookLikePhotonCameras();
};
} // namespace photon

View File

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

View File

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