Add disabled param for PhotonCamera (#2484)

This commit is contained in:
Matt Morley
2026-05-16 07:41:59 -07:00
committed by GitHub
parent 0e834f0851
commit 996ca3649e
14 changed files with 231 additions and 28 deletions

View File

@@ -55,6 +55,10 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
private final Consumer<Integer> fpsLimitConsumer;
private final Supplier<Integer> fpsLimitSupplier;
NTDataChangeListener isEnabledListener;
private final Consumer<Boolean> isEnabledConsumer;
private final BooleanSupplier enabledSupplier;
public NTDataPublisher(
String cameraNickname,
Supplier<Integer> pipelineIndexSupplier,
@@ -62,13 +66,17 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
BooleanSupplier driverModeSupplier,
Consumer<Boolean> driverModeConsumer,
Supplier<Integer> fpsLimitSupplier,
Consumer<Integer> fpsLimitConsumer) {
Consumer<Integer> fpsLimitConsumer,
BooleanSupplier enabledSupplier,
Consumer<Boolean> isEnabledConsumer) {
this.pipelineIndexSupplier = pipelineIndexSupplier;
this.pipelineIndexConsumer = pipelineIndexConsumer;
this.driverModeSupplier = driverModeSupplier;
this.driverModeConsumer = driverModeConsumer;
this.fpsLimitSupplier = fpsLimitSupplier;
this.fpsLimitConsumer = fpsLimitConsumer;
this.enabledSupplier = enabledSupplier;
this.isEnabledConsumer = isEnabledConsumer;
updateCameraNickname(cameraNickname);
updateEntries();
@@ -124,6 +132,19 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
logger.debug("Set FPS limit to " + newFPSLimit);
}
private void onEnabledChange(NetworkTableEvent entryNotification) {
var newEnabled = entryNotification.valueData.value.getBoolean();
var originalEnabled = enabledSupplier.getAsBoolean();
if (newEnabled == originalEnabled) {
logger.debug("Enabled value is already " + newEnabled);
return;
}
isEnabledConsumer.accept(newEnabled);
logger.debug("Set is enabled to " + newEnabled);
}
private void removeEntries() {
if (pipelineIndexListener != null) pipelineIndexListener.remove();
if (driverModeListener != null) driverModeListener.remove();
@@ -148,6 +169,10 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
fpsLimitListener =
new NTDataChangeListener(
ts.subTable.getInstance(), ts.fpsLimitSubscriber, this::onFPSLimitChange);
isEnabledListener =
new NTDataChangeListener(
ts.subTable.getInstance(), ts.enabledSubscriber, this::onEnabledChange);
}
public void updateCameraNickname(String newCameraNickname) {
@@ -198,6 +223,7 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
ts.pipelineIndexPublisher.set(pipelineIndexSupplier.get());
ts.driverModePublisher.set(driverModeSupplier.getAsBoolean());
ts.fpsLimitPublisher.set(fpsLimitSupplier.get());
ts.enabledPublisher.set(enabledSupplier.getAsBoolean());
ts.latencyMillisEntry.set(acceptedResult.getLatencyMillis());
ts.fpsEntry.set(acceptedResult.fps);
ts.hasTargetEntry.set(acceptedResult.hasTargets());

View File

@@ -55,6 +55,7 @@ public class UICameraConfiguration {
public boolean mismatch;
public int fpsLimit;
public boolean isEnabled;
// Status for if the underlying device is present and such
public boolean isConnected;

View File

@@ -88,6 +88,7 @@ public class VisionModule {
private int outputStreamPort = -1;
private int fpsLimit = -1;
private boolean enabled = true;
FileSaveFrameConsumer inputFrameSaver;
FileSaveFrameConsumer outputFrameSaver;
@@ -137,7 +138,8 @@ public class VisionModule {
this::consumeResult,
this.cameraQuirks,
getChangeSubscriber(),
this::getFPSLimit);
this::getFPSLimit,
this::getEnabled);
this.streamRunnable = new StreamRunnable(new OutputStreamPipeline());
changeSubscriberHandle = DataChangeService.getInstance().addSubscriber(changeSubscriber);
@@ -153,7 +155,9 @@ public class VisionModule {
pipelineManager::getDriverMode,
this::setDriverMode,
this::getFPSLimit,
this::setFPSLimit);
this::setFPSLimit,
this::getEnabled,
this::setEnabled);
uiDataConsumer = new UIDataPublisher(visionSource.getSettables().getConfiguration().uniqueName);
statusLEDsConsumer =
new StatusLEDConsumer(visionSource.getSettables().getConfiguration().uniqueName);
@@ -645,6 +649,25 @@ public class VisionModule {
return fpsLimit;
}
/**
* Sets whether the camera is enabled/disabled, disabling the camera allows you to reduce util
* while keeping the vision runner up for fast toggling.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
saveAndBroadcastAll();
}
/**
* Gets whether the camera is enabled or disabled, if disabled the vision runner will still be
* running but the camera will not capture frames, allowing for fast toggling.
*
* @return the enabled state of the camera
*/
public boolean getEnabled() {
return enabled;
}
public CameraConfiguration getStateAsCameraConfig() {
var config = visionSource.getSettables().getConfiguration();
config.setPipelineSettings(pipelineManager.userPipelineSettings);

View File

@@ -50,6 +50,7 @@ public class VisionRunner {
private final List<Runnable> runnableList = new ArrayList<Runnable>();
private final QuirkyCamera cameraQuirks;
private final Supplier<Integer> fpsLimitSupplier;
private final Supplier<Boolean> enabledSupplier;
private long loopCount;
@@ -57,9 +58,14 @@ public class VisionRunner {
* VisionRunner contains a thread to run a pipeline, given a frame, and will give the result to
* the consumer.
*
* @param frameSupplier The supplier of the latest frame.
* @param pipelineSupplier The supplier of the current pipeline.
* @param pipelineResultConsumer The consumer of the latest result.
* @param frameSupplier
* @param pipelineSupplier
* @param pipelineResultConsumer
* @param cameraQuirks
* @param changeSubscriber The subscriber to setting changes for this VisionRunner, so it can
* update its settings when they change.
* @param fpsLimitSupplier
* @param enabledSupplier
*/
public VisionRunner(
FrameProvider frameSupplier,
@@ -67,13 +73,15 @@ public class VisionRunner {
Consumer<CVPipelineResult> pipelineResultConsumer,
QuirkyCamera cameraQuirks,
VisionModuleChangeSubscriber changeSubscriber,
Supplier<Integer> fpsLimitSupplier) {
Supplier<Integer> fpsLimitSupplier,
Supplier<Boolean> enabledSupplier) {
this.frameSupplier = frameSupplier;
this.pipelineSupplier = pipelineSupplier;
this.pipelineResultConsumer = pipelineResultConsumer;
this.cameraQuirks = cameraQuirks;
this.changeSubscriber = changeSubscriber;
this.fpsLimitSupplier = fpsLimitSupplier;
this.enabledSupplier = enabledSupplier;
visionProcessThread = new Thread(this::update);
visionProcessThread.setName("VisionRunner - " + frameSupplier.getName());
@@ -130,6 +138,28 @@ public class VisionRunner {
return future;
}
/**
* Waits until the next time this VisionRunner should run its pipeline, based on current FPS limit
*/
private void waitUntilNextTick(long start) {
int fpsLimit = fpsLimitSupplier.get();
if (fpsLimit > 0) {
long sleepTime = (long) (1000 / fpsLimit - (System.currentTimeMillis() - start));
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
return;
} else {
// Fall through to no limit
return;
}
}
private void update() {
// wait for the camera to connect
while (!frameSupplier.isConnected() && !Thread.interrupted()) {
@@ -194,11 +224,23 @@ public class VisionRunner {
frame.release();
pipelineResultConsumer.accept(new CVPipelineResult(0l, 0, 0, null, new Frame()));
} else if (pipeline == pipelineSupplier.get()) {
if (!enabledSupplier.get()) {
// If we are skipping processing due to the camera being disabled, we still want to send a
// result with the new frame and settings, just with a null pipeline result
pipelineResultConsumer.accept(new CVPipelineResult(0l, 0, 0, null, new Frame()));
frame.release();
continue;
}
// If the pipeline has changed while we are getting our frame we should scrap
// that frame it may result in incorrect frame settings like hsv values
// There's no guarantee the processing type change will occur this tick, so
// pipelines should check themselves
// If we have an FPS limit, check if it's 0, in which case we skip processing and just send
// a blank frame, otherwise we sleep until the next tick
waitUntilNextTick(start);
try {
var pipelineResult = pipeline.run(frame, cameraQuirks);
pipelineResultConsumer.accept(pipelineResult);
@@ -207,18 +249,6 @@ public class VisionRunner {
}
loopCount++;
}
int fpsLimit = fpsLimitSupplier.get();
if (fpsLimit > 0) {
long sleepTime = (long) (1000 / fpsLimit - (System.currentTimeMillis() - start));
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
return;
}
}
}
}
}
}