diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/PipelineOpModeSwitchablePanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/PipelineOpModeSwitchablePanel.kt index 2a8a65ce..97c4222f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/PipelineOpModeSwitchablePanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/PipelineOpModeSwitchablePanel.kt @@ -50,10 +50,10 @@ class PipelineOpModeSwitchablePanel(val eocvSim: EOCVSim) : JTabbedPane() { val index = sourceTabbedPane.selectedIndex if(index == 0) { - opModeSelectorPanel.reset(0) - pipelineSelectorPanel.isActive = true opModeSelectorPanel.isActive = false + + opModeSelectorPanel.reset(0) } else if(index == 1) { opModeSelectorPanel.reset() @@ -76,12 +76,12 @@ class PipelineOpModeSwitchablePanel(val eocvSim: EOCVSim) : JTabbedPane() { fun enableSwitching() { pipelineSelectorPanel.allowPipelineSwitching = true - opModeSelectorPanel.allowOpModeSwitching = true + opModeSelectorPanel.isActive = true } fun disableSwitching() { pipelineSelectorPanel.allowPipelineSwitching = false - opModeSelectorPanel.allowOpModeSwitching = false + opModeSelectorPanel.isActive = false } fun enableSwitchingBlocking() = runBlocking { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt index e8ff4610..18255cbf 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt @@ -8,8 +8,6 @@ import io.github.deltacv.vision.internal.opmode.OpModeNotification import io.github.deltacv.vision.internal.opmode.OpModeState import java.awt.BorderLayout import javax.swing.JPanel -import java.awt.GridBagConstraints -import java.awt.GridBagLayout import javax.swing.JButton import javax.swing.SwingUtilities @@ -21,8 +19,9 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { private set private var currentManagerIndex: Int? = null + private var upcomingIndex: Int? = null - var allowOpModeSwitching = false + var isActive = false init { layout = BorderLayout() @@ -36,6 +35,8 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { eocvSim.pipelineManager.onUpdate.doOnce { if(eocvSim.pipelineManager.currentPipeline !is OpMode) return@doOnce + eocvSim.pipelineManager.setPaused(false, PipelineManager.PauseReason.NOT_PAUSED) + val opMode = eocvSim.pipelineManager.currentPipeline as OpMode val state = opMode.notifier.state @@ -50,17 +51,16 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { } fun stopCurrentOpMode() { - if(eocvSim.pipelineManager.currentPipeline !is OpMode) return - - val opMode = eocvSim.pipelineManager.currentPipeline as OpMode - opMode.notifier.notify(OpModeNotification.STOP) + if(eocvSim.pipelineManager.currentPipeline != currentOpMode || currentOpMode == null) return + currentOpMode!!.notifier.notify(OpModeNotification.STOP) } private fun notifySelected() { - if(!allowOpModeSwitching) return + if(!isActive) return if(eocvSim.pipelineManager.currentPipeline !is OpMode) return val opMode = eocvSim.pipelineManager.currentPipeline as OpMode + val opModeIndex = currentManagerIndex!! opMode.notifier.onStateChange { val state = opMode.notifier.state @@ -70,7 +70,10 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { } if(state == OpModeState.STOPPED) { - opModeSelected(currentManagerIndex!!) + if(isActive && opModeIndex == upcomingIndex) { + opModeSelected(currentManagerIndex!!) + } + it.removeThis() } } @@ -97,12 +100,16 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { } fun opModeSelected(managerIndex: Int, forceChangePipeline: Boolean = true) { - eocvSim.pipelineManager.setPaused(false) + eocvSim.pipelineManager.requestSetPaused(false) + + if(forceChangePipeline) { + eocvSim.pipelineManager.requestForceChangePipeline(managerIndex) + } - if(forceChangePipeline) eocvSim.pipelineManager.requestForceChangePipeline(managerIndex) - currentManagerIndex = managerIndex + upcomingIndex = managerIndex eocvSim.pipelineManager.onUpdate.doOnce { + currentManagerIndex = managerIndex notifySelected() } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt index 79d3e406..f37e4a7e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt @@ -54,15 +54,12 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC val autonomousSelector = JList() val teleopSelector = JList() - var allowOpModeSwitching = false + var isActive = false set(value) { - opModeControlsPanel.allowOpModeSwitching = value + opModeControlsPanel.isActive = value field = value } - var isActive = false - internal set - init { layout = GridBagLayout() selectOpModeLabelsPanel.layout = GridBagLayout() @@ -168,7 +165,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC autonomousSelector.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { - if (!allowOpModeSwitching) return + if (!isActive) return val index = (e.source as JList<*>).locationToIndex(e.point) if(index >= 0) { @@ -179,7 +176,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC teleopSelector.addMouseListener(object: MouseAdapter() { override fun mouseClicked(e: MouseEvent) { - if (!allowOpModeSwitching) return + if (!isActive) return val index = (e.source as JList<*>).locationToIndex(e.point) if(index >= 0) { @@ -189,12 +186,17 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC }) eocvSim.pipelineManager.onPipelineChange { - // we are doing this to detect external pipeline changes + if(!isActive) return@onPipelineChange + + // we are doing this to detect external pipeline changes and reflect them + // accordingly in the UI. + // // in the event that this change was triggered by us, OpModeSelectorPanel, // we need to hold on a cycle so that the state has been fully updated, - // just to be able to check correctly. + // just to be able to check correctly and, if it was requested by + // OpModeSelectorPanel, skip this message and not do anything. eocvSim.pipelineManager.onUpdate.doOnce { - if(isActive && opModeControlsPanel.currentOpMode != eocvSim.pipelineManager.currentPipeline) { + if(isActive && opModeControlsPanel.currentOpMode != eocvSim.pipelineManager.currentPipeline && eocvSim.pipelineManager.currentPipeline != null) { val opMode = eocvSim.pipelineManager.currentPipeline if(opMode is OpMode) { @@ -205,7 +207,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC logger.info("External change detected \"$name\"") opModeSelected(eocvSim.pipelineManager.currentPipelineIndex, name, false) - } else { + } else if(isActive) { reset(-1) } } @@ -222,6 +224,8 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC } private fun opModeSelected(managerIndex: Int, name: String, forceChangePipeline: Boolean = true) { + if(!isActive) return + opModeNameLabel.text = name textPanel.removeAll() @@ -277,9 +281,9 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC opModeControlsPanel.reset() - if(eocvSim.pipelineManager.currentPipeline == opModeControlsPanel.currentOpMode) { - val opMode = opModeControlsPanel.currentOpMode + val opMode = opModeControlsPanel.currentOpMode + if(eocvSim.pipelineManager.currentPipeline == opMode && opMode != null && opMode.notifier.state != OpModeState.SELECTED) { opMode?.notifier?.onStateChange?.let { it { val state = opMode.notifier.state @@ -289,7 +293,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC if(nextPipeline == null || nextPipeline >= 0) { eocvSim.pipelineManager.onUpdate.doOnce { - eocvSim.pipelineManager.requestChangePipeline(nextPipeline) + eocvSim.pipelineManager.changePipeline(nextPipeline) } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java index b064fe2d..5cd65da5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java @@ -109,7 +109,6 @@ public void update(boolean isPaused) { Mat m = currentInputSource.update(); if(m != null && !m.empty()) { - lastMatFromSource.release(); m.copyTo(lastMatFromSource); // add an extra alpha channel because that's what eocv returns for some reason... (more realistic simulation lol) Imgproc.cvtColor(lastMatFromSource, lastMatFromSource, Imgproc.COLOR_RGB2RGBA); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 741c53c7..3c627e6a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -108,6 +108,8 @@ class PipelineManager(var eocvSim: EOCVSim, val pipelineStatisticsCalculator: Pi return field } + var pauseOnImages = true + var pauseReason = PauseReason.NOT_PAUSED private set get() { @@ -615,7 +617,7 @@ class PipelineManager(var eocvSim: EOCVSim, val pipelineStatisticsCalculator: Pi setPaused(false) //if pause on images option is turned on by user - if (eocvSim.configManager.config.pauseOnImages) { + if (eocvSim.configManager.config.pauseOnImages && pauseOnImages) { //pause next frame if current selected input source is an image eocvSim.inputSourceManager.pauseIfImageTwoFrames() } diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptStackProcessor.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptStackProcessor.java deleted file mode 100644 index 54fc7a95..00000000 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptStackProcessor.java +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright (c) 2023 FIRST. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted (subject to the limitations in the disclaimer below) provided that - * the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list - * of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * Neither the name of FIRST nor the names of its contributors may be used to endorse or - * promote products derived from this software without specific prior written permission. - * - * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS - * LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.firstinspires.ftc.robotcontroller.external.samples; - -import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; -import com.qualcomm.robotcore.eventloop.opmode.TeleOp; -import org.firstinspires.ftc.robotcore.external.hardware.camera.BuiltinCameraDirection; -import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; -import org.firstinspires.ftc.teamcode.processors.StackProcessor; -import org.firstinspires.ftc.vision.VisionPortal; -import org.firstinspires.ftc.vision.VisionProcessor; -import org.firstinspires.ftc.vision.apriltag.AprilTagDetection; -import org.firstinspires.ftc.vision.apriltag.AprilTagProcessor; - -import java.util.List; - -/** - * This 2023-2024 OpMode illustrates the basics of AprilTag recognition and pose estimation, - * including Java Builder structures for specifying Vision parameters. - * - * Use Android Studio to Copy this Class, and Paste it into your team's code folder with a new name. - * Remove or comment out the @Disabled line to add this OpMode to the Driver Station OpMode list. - */ -@TeleOp(name = "Concept: Stack Processor", group = "Concept") -// @Disabled -public class ConceptStackProcessor extends LinearOpMode { - - private static final boolean USE_WEBCAM = true; // true for webcam, false for phone camera - - /** - * {@link #aprilTag} is the variable to store our instance of the AprilTag processor. - */ - private VisionProcessor aprilTag; - - /** - * {@link #visionPortal} is the variable to store our instance of the vision portal. - */ - private VisionPortal visionPortal; - - @Override - public void runOpMode() { - - initAprilTag(); - - // Wait for the DS start button to be touched. - telemetry.addData("DS preview on/off", "3 dots, Camera Stream"); - telemetry.addData(">", "Touch Play to start OpMode"); - telemetry.update(); - - waitForStart(); - - if (opModeIsActive()) { - while (opModeIsActive()) { - telemetryAprilTag(); - - // Push telemetry to the Driver Station. - telemetry.update(); - - // Share the CPU. - sleep(20); - } - } - - // Save more CPU resources when camera is no longer needed. - visionPortal.close(); - - } // end method runOpMode() - - /** - * Initialize the AprilTag processor. - */ - private void initAprilTag() { - - // Create the AprilTag processor. - aprilTag = new StackProcessor(); - - // Create the vision portal by using a builder. - VisionPortal.Builder builder = new VisionPortal.Builder(); - - // Set the camera (webcam vs. built-in RC phone camera). - if (USE_WEBCAM) { - builder.setCamera(hardwareMap.get(WebcamName.class, "Webcam 1")); - } else { - builder.setCamera(BuiltinCameraDirection.BACK); - } - - // Choose a camera resolution. Not all cameras support all resolutions. - //builder.setCameraResolution(new Size(640, 480)); - - // Enable the RC preview (LiveView). Set "false" to omit camera monitoring. - //builder.enableCameraMonitoring(true); - - // Set the stream format; MJPEG uses less bandwidth than default YUY2. - //builder.setStreamFormat(VisionPortal.StreamFormat.YUY2); - - // Choose whether or not LiveView stops if no processors are enabled. - // If set "true", monitor shows solid orange screen if no processors enabled. - // If set "false", monitor shows camera view without annotations. - //builder.setAutoStopLiveView(false); - - // Set and enable the processor. - builder.addProcessor(aprilTag); - - // Build the Vision Portal, using the above settings. - visionPortal = builder.build(); - - // Disable or re-enable the aprilTag processor at any time. - //visionPortal.setProcessorEnabled(aprilTag, true); - - } // end method initAprilTag() - - - /** - * Function to add telemetry about AprilTag detections. - */ - private void telemetryAprilTag() { - - } // end method telemetryAprilTag() - -} // end class \ No newline at end of file diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagProcessorPipeline.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagProcessorPipeline.java deleted file mode 100644 index 2ade7b7a..00000000 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagProcessorPipeline.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.firstinspires.ftc.teamcode; - -import android.graphics.Canvas; -import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit; -import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit; -import org.firstinspires.ftc.robotcore.internal.camera.calibration.CameraCalibration; -import org.firstinspires.ftc.vision.apriltag.AprilTagProcessor; -import org.opencv.core.Mat; -import org.openftc.easyopencv.OpenCvPipeline; -import org.openftc.easyopencv.TimestampedOpenCvPipeline; - -public class AprilTagProcessorPipeline extends TimestampedOpenCvPipeline { - - AprilTagProcessor processor = new AprilTagProcessor.Builder() - .setOutputUnits(DistanceUnit.METER, AngleUnit.DEGREES) - .setTagFamily(AprilTagProcessor.TagFamily.TAG_36h11) - .setDrawAxes(true) - .setDrawTagOutline(true) - .setDrawCubeProjection(true) - .build(); - - @Override - public void init(Mat firstFrame) { - processor.init(firstFrame.width(), firstFrame.height(), null); - } - - @Override - public Mat processFrame(Mat input, long captureTimeNanos) { - requestViewportDrawHook(processor.processFrame(input, captureTimeNanos)); - - return input; - } - - @Override - public void onDrawFrame(Canvas canvas, int onscreenWidth, int onscreenHeight, float scaleBmpPxToCanvasPx, float scaleCanvasDensity, Object userContext) { - processor.onDrawFrame(canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext); - } - -} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/processors/StackProcessor.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/processors/StackProcessor.java index 83737f19..defb5efb 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/processors/StackProcessor.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/processors/StackProcessor.java @@ -62,9 +62,6 @@ public Object processFrame(Mat frame, long captureTimeNanos) { double avgHueValue = Core.mean(ringHSV).val[0]; - ring.release(); - ringHSV.release(); - if (avgHueValue > FOUR_STACK_HUE_THRESHOLD) { result = RingNumber.FOUR; } else if (avgHueValue > ONE_STACK_HUE_THRESHOLD) { diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java index 6147cdaa..d1d306be 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java @@ -1,7 +1,6 @@ package io.github.deltacv.vision.external.source; import io.github.deltacv.vision.external.util.Timestamped; -import io.github.deltacv.vision.internal.util.KillException; import org.opencv.core.Mat; import org.opencv.core.Size; import org.slf4j.Logger; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17b4076a..cf5dd7d2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists \ No newline at end of file +zipStorePath=wrapper/dists