diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java index 3a1848c9a..e9773122a 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java @@ -17,20 +17,18 @@ package org.photonvision.vision.frame.consumer; -import edu.wpi.first.networktables.BooleanEntry; +import edu.wpi.first.networktables.IntegerEntry; import edu.wpi.first.networktables.NetworkTable; import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import org.opencv.imgcodecs.Imgcodecs; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; -import org.photonvision.common.util.TimedTaskManager; import org.photonvision.vision.opencv.CVMat; public class FileSaveFrameConsumer implements Consumer { @@ -44,29 +42,27 @@ public class FileSaveFrameConsumer implements Consumer { private NetworkTable subTable; private final NetworkTable rootTable; private final Logger logger; - private boolean prevCommand = false; + private long imgSaveCountInternal = 0; private String camNickname; private String fnamePrefix; - private final long CMD_RESET_TIME_MS = 500; - private final BooleanEntry entry; - // Helps prevent race conditions between user set & auto-reset logic - private ReentrantLock lock; + private IntegerEntry entry; public FileSaveFrameConsumer(String camNickname, String streamPrefix) { - this.lock = new ReentrantLock(); this.fnamePrefix = camNickname + "_" + streamPrefix; this.ntEntryName = streamPrefix + NT_SUFFIX; this.rootTable = NetworkTablesManager.getInstance().kRootTable; updateCameraNickname(camNickname); - entry = subTable.getBooleanTopic(ntEntryName).getEntry(false); this.logger = new Logger(FileSaveFrameConsumer.class, this.camNickname, LogGroup.General); } public void accept(CVMat image) { if (image != null && image.getMat() != null && !image.getMat().empty()) { - if (lock.tryLock()) { - boolean curCommand = entry.get(false); - if (curCommand && !prevCommand) { + var curCommand = entry.get(); // default to just our current count + if (curCommand >= 0) { + // Only do something if we got a valid current command + if (imgSaveCountInternal < curCommand) { + // Save one frame. + // Create the filename Date now = new Date(); String savefile = FILE_PATH @@ -78,42 +74,32 @@ public class FileSaveFrameConsumer implements Consumer { + tf.format(now) + FILE_EXTENSION; + // write to file Imgcodecs.imwrite(savefile, image.getMat()); - // Help the user a bit - set the NT entry back to false after 500ms - TimedTaskManager.getInstance().addOneShotTask(this::resetCommand, CMD_RESET_TIME_MS); - + // Count one more image saved + imgSaveCountInternal++; logger.info("Saved new image at " + savefile); - } else if (!curCommand) { - // If the entry is currently false, set it again. This will make sure it shows up on the - // dashboard. - entry.set(false); + + } else if (imgSaveCountInternal > curCommand) { + imgSaveCountInternal = curCommand; } - - prevCommand = curCommand; - lock.unlock(); - } - } - } - - private void resetCommand() { - lock.lock(); - this.subTable.getEntry(ntEntryName).setBoolean(false); - lock.unlock(); - } - - private void removeEntries() { - if (this.subTable != null) { - if (this.subTable.containsKey(ntEntryName)) { - this.subTable.getEntry(ntEntryName).close(); } } } public void updateCameraNickname(String newCameraNickname) { - removeEntries(); + // Remove existing entries + if (this.subTable != null) { + if (this.subTable.containsKey(ntEntryName)) { + this.subTable.getEntry(ntEntryName).close(); + } + } + + // Recreate and re-init network tables structure this.camNickname = newCameraNickname; this.subTable = rootTable.getSubTable(this.camNickname); - resetCommand(); + this.subTable.getEntry(ntEntryName).setInteger(imgSaveCountInternal); + this.entry = subTable.getIntegerTopic(ntEntryName).getEntry(-1); // Default negative } } diff --git a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java index aa0951f06..7a4946ece 100644 --- a/photon-lib/src/main/java/org/photonvision/PhotonCamera.java +++ b/photon-lib/src/main/java/org/photonvision/PhotonCamera.java @@ -56,7 +56,7 @@ public class PhotonCamera { DoubleArrayPublisher targetPoseEntry; DoublePublisher targetSkewEntry; StringSubscriber versionEntry; - BooleanPublisher inputSaveImgEntry, outputSaveImgEntry; + IntegerEntry inputSaveImgEntry, outputSaveImgEntry; IntegerEntry pipelineIndexEntry, ledModeEntry; IntegerSubscriber heartbeatEntry; @@ -111,8 +111,8 @@ public class PhotonCamera { path = rootTable.getPath(); rawBytesEntry = rootTable.getRawTopic("rawBytes").subscribe("rawBytes", new byte[] {}); driverModeEntry = rootTable.getBooleanTopic("driverMode").getEntry(false); - inputSaveImgEntry = rootTable.getBooleanTopic("inputSaveImgCmd").getEntry(false); - outputSaveImgEntry = rootTable.getBooleanTopic("outputSaveImgCmd").getEntry(false); + inputSaveImgEntry = rootTable.getIntegerTopic("inputSaveImgCmd").getEntry(0); + outputSaveImgEntry = rootTable.getIntegerTopic("outputSaveImgCmd").getEntry(0); pipelineIndexEntry = rootTable.getIntegerTopic("pipelineIndex").getEntry(0); heartbeatEntry = rootTable.getIntegerTopic("heartbeat").subscribe(-1); ledModeEntry = mainTable.getIntegerTopic("ledMode").getEntry(-1); @@ -181,7 +181,7 @@ public class PhotonCamera { * /opt/photonvision/photonvision_config/imgSaves frequently to prevent issues. */ public void takeInputSnapshot() { - inputSaveImgEntry.set(true); + inputSaveImgEntry.set(inputSaveImgEntry.get() + 1); } /** @@ -191,7 +191,7 @@ public class PhotonCamera { * /opt/photonvision/photonvision_config/imgSaves frequently to prevent issues. */ public void takeOutputSnapshot() { - outputSaveImgEntry.set(true); + outputSaveImgEntry.set(outputSaveImgEntry.get() + 1); } /** diff --git a/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp b/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp index f885b9b2a..4e81d5d7e 100644 --- a/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp +++ b/photon-lib/src/main/native/cpp/photonlib/PhotonCamera.cpp @@ -41,9 +41,13 @@ PhotonCamera::PhotonCamera(std::shared_ptr instance, rawBytesEntry(rootTable->GetRawTopic("rawBytes").Subscribe("raw", {})), driverModeEntry(rootTable->GetBooleanTopic("driverMode").Publish()), inputSaveImgEntry( - rootTable->GetBooleanTopic("inputSaveImgCmd").Publish()), + rootTable->GetIntegerTopic("inputSaveImgCmd").Publish()), + inputSaveImgSubscriber( + rootTable->GetIntegerTopic("inputSaveImgCmd").Subscribe(0)), outputSaveImgEntry( - rootTable->GetBooleanTopic("outputSaveImgCmd").Publish()), + rootTable->GetIntegerTopic("outputSaveImgCmd").Publish()), + outputSaveImgSubscriber( + rootTable->GetIntegerTopic("outputSaveImgCmd").Subscribe(0)), pipelineIndexEntry(rootTable->GetIntegerTopic("pipelineIndex").Publish()), ledModeEntry(mainTable->GetIntegerTopic("ledMode").Publish()), versionEntry(mainTable->GetStringTopic("version").Subscribe("")), @@ -89,9 +93,13 @@ void PhotonCamera::SetDriverMode(bool driverMode) { driverModeEntry.Set(driverMode); } -void PhotonCamera::TakeInputSnapshot() { inputSaveImgEntry.Set(true); } +void PhotonCamera::TakeInputSnapshot() { + inputSaveImgEntry.Set(inputSaveImgSubscriber.Get() + 1); +} -void PhotonCamera::TakeOutputSnapshot() { outputSaveImgEntry.Set(true); } +void PhotonCamera::TakeOutputSnapshot() { + outputSaveImgEntry.Set(outputSaveImgSubscriber.Get() + 1); +} bool PhotonCamera::GetDriverMode() const { return driverModeSubscriber.Get(); } diff --git a/photon-lib/src/main/native/include/photonlib/PhotonCamera.h b/photon-lib/src/main/native/include/photonlib/PhotonCamera.h index 0d0bb134b..a1ce0ecd4 100644 --- a/photon-lib/src/main/native/include/photonlib/PhotonCamera.h +++ b/photon-lib/src/main/native/include/photonlib/PhotonCamera.h @@ -166,8 +166,10 @@ class PhotonCamera { std::shared_ptr rootTable; nt::RawSubscriber rawBytesEntry; nt::BooleanPublisher driverModeEntry; - nt::BooleanPublisher inputSaveImgEntry; - nt::BooleanPublisher outputSaveImgEntry; + nt::IntegerPublisher inputSaveImgEntry; + nt::IntegerSubscriber inputSaveImgSubscriber; + nt::IntegerPublisher outputSaveImgEntry; + nt::IntegerSubscriber outputSaveImgSubscriber; nt::IntegerPublisher pipelineIndexEntry; nt::IntegerPublisher ledModeEntry; nt::StringSubscriber versionEntry;