From d754b1300f7374b4d394ea7bb9a852288fdd430a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Jun 2025 03:13:20 +0000 Subject: [PATCH 1/3] Initial plan for issue From 232b48a095483f0c75fe958840dd483d5d36658e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Jun 2025 03:18:00 +0000 Subject: [PATCH 2/3] Implement triple-down detection and runtime settings menu Co-authored-by: Voidless7125 <167136334+Voidless7125@users.noreply.github.com> --- src/comptition/comptition.cpp | 133 +++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/src/comptition/comptition.cpp b/src/comptition/comptition.cpp index fb7c352..a1ff514 100644 --- a/src/comptition/comptition.cpp +++ b/src/comptition/comptition.cpp @@ -104,6 +104,96 @@ void displayDriveModeMenu() primaryController.Screen.print("Drive Mode Selected"); } +// Function to display runtime settings menu +void displayRuntimeSettingsMenu() +{ + primaryController.Screen.clearScreen(); + primaryController.Screen.setCursor(1, 1); + primaryController.Screen.print("Runtime Settings"); + + // Simple menu with numbered options + primaryController.Screen.setCursor(2, 1); + primaryController.Screen.print("A: Drive Mode"); + primaryController.Screen.setCursor(3, 1); + primaryController.Screen.print("B: Traction Ctrl"); + primaryController.Screen.setCursor(4, 1); + primaryController.Screen.print("X: Stability Ctrl"); + primaryController.Screen.setCursor(5, 1); + primaryController.Screen.print("Y: ABS Control"); + + vex::timer menuTimer; + bool menuActive = true; + + while (menuActive && Competition.isEnabled() && menuTimer.time() < 10000) // 10 second timeout + { + if (primaryController.ButtonA.pressing()) + { + // Cycle through drive modes + configManager::DriveMode currentMode = ConfigManager.getDriveMode(); + configManager::DriveMode newMode; + + switch (currentMode) + { + case configManager::DriveMode::LeftArcade: + newMode = configManager::DriveMode::RightArcade; + break; + case configManager::DriveMode::RightArcade: + newMode = configManager::DriveMode::SplitArcade; + break; + case configManager::DriveMode::SplitArcade: + newMode = configManager::DriveMode::Tank; + break; + case configManager::DriveMode::Tank: + newMode = configManager::DriveMode::LeftArcade; + break; + } + + ConfigManager.setDriveMode(newMode); + primaryController.Screen.clearScreen(); + primaryController.Screen.setCursor(1, 1); + primaryController.Screen.print("Drive Mode Changed"); + vex::this_thread::sleep_for(1000); + menuActive = false; + } + else if (primaryController.ButtonB.pressing()) + { + tractionControlEnabled = !tractionControlEnabled; + primaryController.Screen.clearScreen(); + primaryController.Screen.setCursor(1, 1); + primaryController.Screen.print(tractionControlEnabled ? "Traction ON" : "Traction OFF"); + vex::this_thread::sleep_for(1000); + menuActive = false; + } + else if (primaryController.ButtonX.pressing()) + { + stabilityControlEnabled = !stabilityControlEnabled; + primaryController.Screen.clearScreen(); + primaryController.Screen.setCursor(1, 1); + primaryController.Screen.print(stabilityControlEnabled ? "Stability ON" : "Stability OFF"); + vex::this_thread::sleep_for(1000); + menuActive = false; + } + else if (primaryController.ButtonY.pressing()) + { + absEnabled = !absEnabled; + primaryController.Screen.clearScreen(); + primaryController.Screen.setCursor(1, 1); + primaryController.Screen.print(absEnabled ? "ABS ON" : "ABS OFF"); + vex::this_thread::sleep_for(1000); + menuActive = false; + } + else if (primaryController.ButtonDown.pressing() || primaryController.ButtonUp.pressing()) + { + // Exit menu if Down or Up is pressed + menuActive = false; + } + + vex::this_thread::sleep_for(50); + } + + primaryController.Screen.clearScreen(); +} + // User control task void userControl() { @@ -118,6 +208,12 @@ void userControl() double turnVolts, forwardVolts; bool configMenuActive = false; // Tracks if options menu is active + + // Triple-down detection variables + int downPressCount = 0; + vex::timer downPressTimer; + bool lastDownState = false; + const int TRIPLE_DOWN_WINDOW_MS = 1500; // 1.5 second window // Load drive mode from config configManager::DriveMode currentDriveMode = ConfigManager.getDriveMode(); @@ -127,7 +223,42 @@ void userControl() while (Competition.isEnabled()) { - // Open configuration menu + // Triple-down detection for runtime settings + bool currentDownState = primaryController.ButtonDown.pressing(); + + if (currentDownState && !lastDownState) // Down button just pressed + { + if (downPressCount == 0 || downPressTimer.time() <= TRIPLE_DOWN_WINDOW_MS) + { + downPressCount++; + downPressTimer.clear(); + + if (downPressCount >= 3) + { + // Triple-down detected, open runtime settings + displayRuntimeSettingsMenu(); + currentDriveMode = ConfigManager.getDriveMode(); // Update current mode + leftDeadzone = ConfigManager.getLeftDeadzone(); // Update deadzones if changed + rightDeadzone = ConfigManager.getRightDeadzone(); + downPressCount = 0; // Reset counter + } + } + else + { + // Window expired, reset counter + downPressCount = 1; + downPressTimer.clear(); + } + } + else if (downPressTimer.time() > TRIPLE_DOWN_WINDOW_MS && downPressCount > 0) + { + // Reset counter if window expires + downPressCount = 0; + } + + lastDownState = currentDownState; + + // Open configuration menu (existing UP button functionality) if (primaryController.ButtonUp.pressing()) { configMenuActive = !configMenuActive; From 2a26abad21c84e3a67199ee2fd9630261009d1a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Jun 2025 03:20:19 +0000 Subject: [PATCH 3/3] Add button debouncing, user feedback, and test validation Co-authored-by: Voidless7125 <167136334+Voidless7125@users.noreply.github.com> --- src/comptition/comptition.cpp | 106 ++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/src/comptition/comptition.cpp b/src/comptition/comptition.cpp index a1ff514..e9a6f9c 100644 --- a/src/comptition/comptition.cpp +++ b/src/comptition/comptition.cpp @@ -1,5 +1,58 @@ #include "vex.h" +// Test function to validate triple-down logic (for development testing) +void testTripleDownLogic() +{ + logHandler("testTripleDownLogic", "Testing triple-down detection logic", Log::Level::Info); + + // Test variables + int downPressCount = 0; + vex::timer downPressTimer; + const int TRIPLE_DOWN_WINDOW_MS = 1500; + + // Test case 1: Quick triple press (should work) + downPressCount = 0; + downPressTimer.clear(); + + // Simulate first press + downPressCount++; + if (downPressCount < 3) downPressTimer.clear(); + + // Simulate second press after 300ms + vex::this_thread::sleep_for(300); + if (downPressTimer.time() <= TRIPLE_DOWN_WINDOW_MS) { + downPressCount++; + if (downPressCount < 3) downPressTimer.clear(); + } + + // Simulate third press after another 300ms + vex::this_thread::sleep_for(300); + if (downPressTimer.time() <= TRIPLE_DOWN_WINDOW_MS) { + downPressCount++; + if (downPressCount >= 3) { + logHandler("testTripleDownLogic", "Test case 1 PASSED: Quick triple press detected", Log::Level::Info); + } + } + + // Test case 2: Slow triple press (should fail) + downPressCount = 0; + downPressTimer.clear(); + + downPressCount++; + vex::this_thread::sleep_for(800); // Wait longer + if (downPressTimer.time() <= TRIPLE_DOWN_WINDOW_MS) { + downPressCount++; + vex::this_thread::sleep_for(800); // Wait longer again + if (downPressTimer.time() <= TRIPLE_DOWN_WINDOW_MS) { + downPressCount++; + } else { + logHandler("testTripleDownLogic", "Test case 2 PASSED: Slow triple press correctly rejected", Log::Level::Info); + } + } + + logHandler("testTripleDownLogic", "Triple-down logic testing completed", Log::Level::Info); +} + void autonomous() { logHandler("autonomous", "Test message.", Log::Level::Warn, 2); @@ -123,11 +176,25 @@ void displayRuntimeSettingsMenu() vex::timer menuTimer; bool menuActive = true; + bool buttonWasPressed = false; // Debounce flag while (menuActive && Competition.isEnabled() && menuTimer.time() < 10000) // 10 second timeout { - if (primaryController.ButtonA.pressing()) + bool anyButtonPressed = primaryController.ButtonA.pressing() || + primaryController.ButtonB.pressing() || + primaryController.ButtonX.pressing() || + primaryController.ButtonY.pressing() || + primaryController.ButtonDown.pressing() || + primaryController.ButtonUp.pressing(); + + if (!anyButtonPressed) { + buttonWasPressed = false; // Reset debounce when no button is pressed + } + + if (!buttonWasPressed && primaryController.ButtonA.pressing()) + { + buttonWasPressed = true; // Cycle through drive modes configManager::DriveMode currentMode = ConfigManager.getDriveMode(); configManager::DriveMode newMode; @@ -151,12 +218,31 @@ void displayRuntimeSettingsMenu() ConfigManager.setDriveMode(newMode); primaryController.Screen.clearScreen(); primaryController.Screen.setCursor(1, 1); - primaryController.Screen.print("Drive Mode Changed"); + + std::string modeText; + switch (newMode) + { + case configManager::DriveMode::LeftArcade: + modeText = "Left Arcade"; + break; + case configManager::DriveMode::RightArcade: + modeText = "Right Arcade"; + break; + case configManager::DriveMode::SplitArcade: + modeText = "Split Arcade"; + break; + case configManager::DriveMode::Tank: + modeText = "Tank Drive"; + break; + } + + primaryController.Screen.print(("Mode: " + modeText).c_str()); vex::this_thread::sleep_for(1000); menuActive = false; } - else if (primaryController.ButtonB.pressing()) + else if (!buttonWasPressed && primaryController.ButtonB.pressing()) { + buttonWasPressed = true; tractionControlEnabled = !tractionControlEnabled; primaryController.Screen.clearScreen(); primaryController.Screen.setCursor(1, 1); @@ -164,8 +250,9 @@ void displayRuntimeSettingsMenu() vex::this_thread::sleep_for(1000); menuActive = false; } - else if (primaryController.ButtonX.pressing()) + else if (!buttonWasPressed && primaryController.ButtonX.pressing()) { + buttonWasPressed = true; stabilityControlEnabled = !stabilityControlEnabled; primaryController.Screen.clearScreen(); primaryController.Screen.setCursor(1, 1); @@ -173,8 +260,9 @@ void displayRuntimeSettingsMenu() vex::this_thread::sleep_for(1000); menuActive = false; } - else if (primaryController.ButtonY.pressing()) + else if (!buttonWasPressed && primaryController.ButtonY.pressing()) { + buttonWasPressed = true; absEnabled = !absEnabled; primaryController.Screen.clearScreen(); primaryController.Screen.setCursor(1, 1); @@ -182,8 +270,9 @@ void displayRuntimeSettingsMenu() vex::this_thread::sleep_for(1000); menuActive = false; } - else if (primaryController.ButtonDown.pressing() || primaryController.ButtonUp.pressing()) + else if (!buttonWasPressed && (primaryController.ButtonDown.pressing() || primaryController.ButtonUp.pressing())) { + buttonWasPressed = true; // Exit menu if Down or Up is pressed menuActive = false; } @@ -242,6 +331,11 @@ void userControl() rightDeadzone = ConfigManager.getRightDeadzone(); downPressCount = 0; // Reset counter } + else if (downPressCount == 2) + { + // Give user feedback they're partway there + primaryController.rumble("-"); + } } else {