mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-03 03:01:40 +00:00
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:
@@ -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!",
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user