From 4aaeb59493dd0f9fee9c618293cf58a7255daf53 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:00:01 +0000 Subject: [PATCH 1/4] Initial plan From 6186c8f6f3d6cb9493cb1f5349aab6d86e011d0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:04:24 +0000 Subject: [PATCH 2/4] Add comprehensive State Transitions and Commands sections to StateMachine guide Co-authored-by: TaylerUva <8679670+TaylerUva@users.noreply.github.com> --- Lessons (Software)/StateMachine.md | 279 +++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/Lessons (Software)/StateMachine.md b/Lessons (Software)/StateMachine.md index e3be911c..d62d9509 100644 --- a/Lessons (Software)/StateMachine.md +++ b/Lessons (Software)/StateMachine.md @@ -134,3 +134,282 @@ This diagram shows a real-world application used by Team 3255. 7. **Assign Buttons:** Assign buttons to trigger state transitions. 8. **Identify Conditions:** Identify transition conditions, including inputs. 9. **Create Diagram:** Create a diagram of the states and transitions to show how the completion of one command leads to the next. + +--- + +## State Transitions + +State transitions are the core mechanism that moves the robot from one state to another. Understanding how to properly implement transitions is critical to a functioning state machine. + +### How to Implement State Transitions + +State transitions typically happen in one of two ways: + +1. **Button/Input-Triggered Transitions:** The operator or driver presses a button to request a state change +2. **Condition-Based Transitions:** The robot automatically transitions when certain conditions are met (sensors, timers, command completion) + +### Transition Logic Flow + +```java +// Example: Setting a target state via button press +if (operatorController.getAButton()) { + currentTargetState = States.INTAKE; +} + +// Check if we can transition to the target state +if (currentTargetState != currentState) { + if (canTransitionTo(currentTargetState)) { + currentState = currentTargetState; + // Execute the command for the new state + } +} +``` + +### Best Practices for Transitions + +* **Validate Transitions:** Always check that preconditions are met before transitioning +* **Avoid Direct Jumps:** Some states may need intermediate states (e.g., can't go from "Intake" to "Shoot" without "Store") +* **Use Target States:** Allow operators to request a state, but only transition when safe/ready +* **Sensor Feedback:** Use sensor data to confirm when a state is complete before transitioning + +--- + +## Commands in State Machines + +Commands are the building blocks that execute actions for each state. Every state typically has an associated command that controls what the robot does while in that state. + +### Command Structure + +WPILib commands have four key methods that you need to understand: + +#### `initialize()` + +The `initialize()` method runs **once** when the command starts. + +**What to put in `initialize()`:** + +* Reset/zero encoders or timers if needed +* Set initial motor speeds or positions +* Record starting sensor values +* Log that the command has started +* Set any flags or state variables + +**Example:** + +```java +@Override +public void initialize() { + // Reset the intake motor position + subIntake.resetEncoder(); + + // Log the command start + System.out.println("Intake command initialized"); + + // Set initial state + hasDetectedGamePiece = false; +} +``` + +!!! warning + Don't put continuous actions in `initialize()`. This method only runs once! + +#### `execute()` + +The `execute()` method runs **continuously** (every 20ms) while the command is active. + +**What to put in `execute()`:** + +* Set motor speeds or positions +* Read sensor values +* Update control loops +* Perform continuous actions + +**Example:** + +```java +@Override +public void execute() { + // Run intake motors + subIntake.setSpeed(constIntake.INTAKE_SPEED); + + // Check for game piece detection + if (subIntake.hasGamePiece()) { + hasDetectedGamePiece = true; + } +} +``` + +#### `end(boolean interrupted)` + +The `end()` method runs **once** when the command finishes (either normally or when interrupted). + +**What to put in `end()`:** + +* Stop motors (set to zero or neutral) +* Save final positions or states +* Clean up resources +* Log completion status +* Reset any temporary flags + +**Example:** + +```java +@Override +public void end(boolean interrupted) { + // Stop the intake motors + subIntake.stop(); + + // Log completion + if (interrupted) { + System.out.println("Intake command was interrupted"); + } else { + System.out.println("Intake command completed successfully"); + } +} +``` + +!!! important + Always stop motors in the `end()` method to prevent them from continuing to run! + +#### `isFinished()` + +The `isFinished()` method runs **continuously** and determines when the command should end. + +**When to return `true`:** + +* When using **sensor-based completion**: Check if sensor conditions are met +* When using **time-based completion**: Check if enough time has elapsed +* When using **position-based completion**: Check if the mechanism reached the target position + +**When to return `false`:** + +* When the command should run indefinitely (until interrupted) +* When the completion condition hasn't been met yet + +**Example 1 - Sensor Check (Preferred for most cases):** + +```java +@Override +public boolean isFinished() { + // Finish when we detect a game piece + return subIntake.hasGamePiece(); +} +``` + +**Example 2 - Time-Based:** + +```java +private Timer timer = new Timer(); + +@Override +public void initialize() { + timer.restart(); +} + +@Override +public boolean isFinished() { + // Finish after 2 seconds + return timer.hasElapsed(2.0); +} +``` + +**Example 3 - Position-Based:** + +```java +@Override +public boolean isFinished() { + // Finish when elevator reaches target position + return Math.abs(subElevator.getPosition() - targetPosition) < constElevator.POSITION_TOLERANCE; +} +``` + +**Example 4 - Never Finish (Manual Control):** + +```java +@Override +public boolean isFinished() { + // This command runs until interrupted by another command + return false; +} +``` + +### Sensor Checks vs. Returning True + +!!! tip "When to use sensor checks vs. always returning true" + + **Use sensor checks (`isFinished()` returns sensor value):** + + * For autonomous actions that need confirmation (intaking, scoring, etc.) + * When you want the robot to automatically proceed to the next state + * When safety requires verification before continuing + + **Always return true:** + + * For instant state changes (prep commands that just set positions) + * When the command completes immediately in `initialize()` + + **Always return false:** + + * For manual control commands (teleoperated driving) + * For commands that should run until interrupted by the operator + +### Command Lifecycle Example + +Here's a complete example showing all four methods working together: + +```java +public class IntakeCommand extends Command { + private final Intake subIntake; + private Timer timeout; + + public IntakeCommand(Intake intake) { + this.subIntake = intake; + addRequirements(intake); + timeout = new Timer(); + } + + @Override + public void initialize() { + // Start fresh + subIntake.resetSensors(); + timeout.restart(); + System.out.println("Starting intake..."); + } + + @Override + public void execute() { + // Continuously run intake motors + subIntake.setSpeed(constIntake.INTAKE_SPEED); + } + + @Override + public void end(boolean interrupted) { + // Always stop motors when done + subIntake.stop(); + + if (interrupted) { + System.out.println("Intake interrupted"); + } else { + System.out.println("Game piece acquired!"); + } + } + + @Override + public boolean isFinished() { + // Finish when sensor detects game piece OR timeout + return subIntake.hasGamePiece() || timeout.hasElapsed(3.0); + } +} +``` + +--- + +## Putting It All Together + +When building a state machine with commands: + +1. **Define your states** based on what the robot needs to accomplish +2. **Create commands** for each state with proper `initialize()`, `execute()`, `end()`, and `isFinished()` methods +3. **Implement transitions** between states based on button inputs and sensor feedback +4. **Use `isFinished()`** to determine when commands complete and trigger automatic transitions +5. **Test thoroughly** to ensure smooth transitions and proper command behavior From b567dee55a1f245cf45aa87e0501b8ce8a0c5022 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:05:06 +0000 Subject: [PATCH 3/4] Fix code review issues: use getAButtonPressed() and initialize Timer at field declaration Co-authored-by: TaylerUva <8679670+TaylerUva@users.noreply.github.com> --- Lessons (Software)/StateMachine.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lessons (Software)/StateMachine.md b/Lessons (Software)/StateMachine.md index d62d9509..a808d4a8 100644 --- a/Lessons (Software)/StateMachine.md +++ b/Lessons (Software)/StateMachine.md @@ -152,7 +152,7 @@ State transitions typically happen in one of two ways: ```java // Example: Setting a target state via button press -if (operatorController.getAButton()) { +if (operatorController.getAButtonPressed()) { currentTargetState = States.INTAKE; } @@ -360,12 +360,11 @@ Here's a complete example showing all four methods working together: ```java public class IntakeCommand extends Command { private final Intake subIntake; - private Timer timeout; + private final Timer timeout = new Timer(); public IntakeCommand(Intake intake) { this.subIntake = intake; addRequirements(intake); - timeout = new Timer(); } @Override From 31c3c91fc0c210ee2629cab1bbc21243db758c06 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:18:03 +0000 Subject: [PATCH 4/4] Fix: Remove motor stopping from end() method for state machine pattern Co-authored-by: TaylerUva <8679670+TaylerUva@users.noreply.github.com> --- Lessons (Software)/StateMachine.md | 82 +++++++++++++++++++----------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/Lessons (Software)/StateMachine.md b/Lessons (Software)/StateMachine.md index a808d4a8..f6f40aab 100644 --- a/Lessons (Software)/StateMachine.md +++ b/Lessons (Software)/StateMachine.md @@ -188,8 +188,8 @@ The `initialize()` method runs **once** when the command starts. **What to put in `initialize()`:** +* **Start motors** - Set motor speeds or positions for this state * Reset/zero encoders or timers if needed -* Set initial motor speeds or positions * Record starting sensor values * Log that the command has started * Set any flags or state variables @@ -207,35 +207,41 @@ public void initialize() { // Set initial state hasDetectedGamePiece = false; + + // Start the motors for this state + subIntake.setSpeed(constIntake.INTAKE_SPEED); } ``` -!!! warning - Don't put continuous actions in `initialize()`. This method only runs once! +!!! important "State Machine Pattern" + In a state machine, `initialize()` should set up motors for the new state. The previous state's `end()` does NOT stop motors - control transfers directly to the new state's `initialize()`. #### `execute()` The `execute()` method runs **continuously** (every 20ms) while the command is active. -**What to put in `execute()`:** +**What to put in `execute()` for state machines:** + +* Monitor sensor values +* Update control loops based on feedback +* Adjust motor speeds if needed (fine-tuning) +* Perform continuous calculations -* Set motor speeds or positions -* Read sensor values -* Update control loops -* Perform continuous actions +!!! note + In many state machine commands, `execute()` can be empty or minimal since motors are set up in `initialize()` and run until the state changes. **Example:** ```java @Override public void execute() { - // Run intake motors - subIntake.setSpeed(constIntake.INTAKE_SPEED); - - // Check for game piece detection + // Monitor for game piece detection if (subIntake.hasGamePiece()) { hasDetectedGamePiece = true; } + + // Motors are already running from initialize() + // Only adjust if needed based on feedback } ``` @@ -243,33 +249,40 @@ public void execute() { The `end()` method runs **once** when the command finishes (either normally or when interrupted). -**What to put in `end()`:** +!!! warning "State Machine Exception" + In a state machine, **DO NOT** use the `end()` method to stop motors. The next state's command will take control of the motors. Stopping motors in `end()` can cause unwanted behavior during state transitions. + +**What to put in `end()` for state machines:** + +* Log completion status (optional) +* Clean up non-motor resources if needed +* Reset temporary flags (if necessary) + +**What NOT to put in `end()` for state machines:** -* Stop motors (set to zero or neutral) -* Save final positions or states -* Clean up resources -* Log completion status -* Reset any temporary flags +* ❌ Motor stop commands +* ❌ Setting motor speeds to zero +* ❌ Changing motor states **Example:** ```java @Override public void end(boolean interrupted) { - // Stop the intake motors - subIntake.stop(); - - // Log completion + // Log completion (optional) if (interrupted) { System.out.println("Intake command was interrupted"); } else { System.out.println("Intake command completed successfully"); } + + // DO NOT stop motors here in a state machine + // The next state's initialize() will take control } ``` -!!! important - Always stop motors in the `end()` method to prevent them from continuing to run! +!!! note + For non-state machine commands (like standalone commands), you should stop motors in `end()`. But in a state machine, motor control is handled by state transitions. #### `isFinished()` @@ -355,7 +368,7 @@ public boolean isFinished() { ### Command Lifecycle Example -Here's a complete example showing all four methods working together: +Here's a complete example showing all four methods working together in a state machine: ```java public class IntakeCommand extends Command { @@ -369,28 +382,35 @@ public class IntakeCommand extends Command { @Override public void initialize() { - // Start fresh + // Start fresh - set up for this state subIntake.resetSensors(); timeout.restart(); System.out.println("Starting intake..."); + + // Start the motors for this state + subIntake.setSpeed(constIntake.INTAKE_SPEED); } @Override public void execute() { - // Continuously run intake motors - subIntake.setSpeed(constIntake.INTAKE_SPEED); + // Continuously monitor and maintain state + // Motor speed is already set in initialize() + + // Could adjust based on feedback if needed + // subIntake.adjustSpeed(...); } @Override public void end(boolean interrupted) { - // Always stop motors when done - subIntake.stop(); - + // Log completion (optional) if (interrupted) { System.out.println("Intake interrupted"); } else { System.out.println("Game piece acquired!"); } + + // DO NOT stop motors here + // The next state will take control } @Override