Skip to content

Conversation

@Strflightmight09
Copy link
Member

No description provided.

@Strflightmight09
Copy link
Member Author

Approved by me as long as you've tested everything
In the meantime also review the rest of the PR and @Nummun14 and @noamgreen12 please review everything

Copy link
Contributor

@Trigon5990 Trigon5990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a ton of jdocs. Make motor subsystem more abstract for this to work with it

Copy link
Member Author

@Strflightmight09 Strflightmight09 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple things:

  1. You forgot to add in javadocs for the actual subsystems themselves
  2. Each of the javadocs for the configs should be a full sentence, and should start with "the" in most cases and shouldn't be in point form

Comment on lines 15 to 28
/**
* Maximum velocity of the motor.
*/
public double maximumVelocity = 1;

/**
* Maximum acceleration of the motor.
*/
public double maximumAcceleration = 1;

/**
* Maximum jerk of the motor.
*/
public double maximumJerk = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Units?

Comment on lines 51 to 54
/**
* Visual offset for display in Mechanism2D.
*/
public double visualizationOffset = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs more than just a rewording, this needs an explanation

Comment on lines 66 to 69
/**
* Acceptable angular tolerance for reaching the target.
*/
public Rotation2d angleTolerance = Rotation2d.kZero;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for reaching the target isn't precise enough


public boolean shouldSimulateGravity = true;

public Color mechanismColor = Color.kBlue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public Color mechanismColor = Color.kBlue;
public Color mechanism2DColor = Color.kBlue;

Rn it sounds like the physical colour of the mechanism

public Color mechanismColor = Color.kBlue;

/**
* The type and number of motors used in the simulation.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* The type and number of motors used in the simulation.
* The type and number of motors used for the simulation.

Comment on lines 155 to 160
/**
* An interface for an arm state.
* getTargetAngle represents the target angle of the state.
* getSpeedScalar represents the speed scalar of the state.
*/
public interface ArmState {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still don't explain what it's for

Comment on lines 8 to 11
/**
* Acceptable position error in meters.
*/
public double positionToleranceMeters = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't consistent with how you described the arm one


public double gearRatio = 1;
/**
* Mass of the Elevator in kg.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kilograms

*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what?

Comment on lines 49 to 52
/**
* should the motor control mode be Voltage as opposed to Velocity.
*/
public boolean shouldUseVoltageControl = false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain what each one is

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Walkthrough

Adds an abstract MotorSubsystem base class with a central registry, global disabled-trigger braking, async brake setting, periodic update hooks, SysId routine integration, extensible logging, and a stop() contract. Implements three concrete subsystems: ArmSubsystem, ElevatorSubsystem, and SimpleMotorSubsystem, each with corresponding Configuration and Commands classes. Each subsystem wires motor control, simulation, SysId configuration, mechanism visualization, state-targeting APIs, logging hooks, and command factory utilities for debugging and state transitions.

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Subsystem Wrapper' is vague and generic, failing to describe the specific changes introduced in this substantial changeset. Use a more descriptive title that captures the main additions, such as 'Add MotorSubsystem base class with arm, elevator, and flywheel implementations' or 'Introduce MotorSubsystem framework with concrete subsystem implementations'.
Description check ❓ Inconclusive No pull request description was provided by the author; the description field is empty. Add a description explaining the purpose of the new MotorSubsystem framework, the subsystems being added (arm, elevator, flywheel), and the key features such as SysId support, logging, and state management.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (1)
hardware/subsystems/arm/ArmConfiguration.java (1)

15-28: Units still missing in velocity/acceleration/jerk JavaDocs.

These fields lack unit specifications. Based on the context (arm angular motion), these should likely be in radians/s, radians/s², and radians/s³ respectively.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 553e791 and 7240380.

📒 Files selected for processing (6)
  • hardware/subsystems/arm/ArmConfiguration.java
  • hardware/subsystems/arm/ArmSubsystem.java
  • hardware/subsystems/elevator/ElevatorConfiguration.java
  • hardware/subsystems/elevator/ElevatorSubsystem.java
  • hardware/subsystems/flywheel/SimpleMotorConfiguration.java
  • hardware/subsystems/flywheel/SimpleMotorSubsystem.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: ## Review Style

  • Explain issues clearly, but don't over-explain. Don't write anything unnecessary or repetetive
  • Suggest a solution when helpful
  • No code blocks unless they genuinely add clarity - GitHub already shows the diff
  • Don't describe what the code does - focus on what could be improved

Naming & Readability

  • Variable, function, and class names must be descriptive and intention-revealing
  • No abbreviations unless universally understood (e.g., id, url)
  • Boolean names should read as questions (isValid, hasPermission, canExecute)
  • Functions should be verbs, classes should be nouns
  • Magic numbers and strings should be named constants
  • Code should be self-documenting; comments should explain "why", not "what"

Clean Code Principles

  • Functions should do one thing and be small
  • Too many parameters? Suggest grouping into an object
  • Flag deep nesting - suggest early returns or extraction
  • DRY: flag duplicated logic
  • Functions with non-obvious side effects
  • Single responsibility for classes and modules
  • Proper abstraction levels - no mixing high and low level logic

Logic & Correctness

  • Off-by-one errors, null/undefined handling, race conditions
  • Unhandled edge cases
  • Incorrect boolean logic or conditions
  • Resource leaks, missing cleanup
  • Error handling gaps
  • Performance issues when significant

Code Structure

  • File and folder organization
  • Proper separation of concerns
  • Dependency management and coupling

Files:

  • hardware/subsystems/flywheel/SimpleMotorConfiguration.java
  • hardware/subsystems/elevator/ElevatorConfiguration.java
  • hardware/subsystems/arm/ArmConfiguration.java
  • hardware/subsystems/elevator/ElevatorSubsystem.java
  • hardware/subsystems/flywheel/SimpleMotorSubsystem.java
  • hardware/subsystems/arm/ArmSubsystem.java
🧬 Code graph analysis (2)
hardware/subsystems/flywheel/SimpleMotorSubsystem.java (3)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SimpleMotorSimulation.java (1)
  • SimpleMotorSimulation (12-81)
utilities/mechanisms/SpeedMechanism2d.java (1)
  • SpeedMechanism2d (12-129)
hardware/subsystems/arm/ArmSubsystem.java (4)
hardware/RobotHardwareStats.java (1)
  • RobotHardwareStats (6-78)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SingleJointedArmSimulation.java (1)
  • SingleJointedArmSimulation (12-81)
utilities/mechanisms/SingleJointedArmMechanism2d.java (1)
  • SingleJointedArmMechanism2d (14-87)
🔇 Additional comments (4)
hardware/subsystems/elevator/ElevatorSubsystem.java (2)

89-96: Mechanism update may use wrong units.

getPositionRotations() returns motor rotations, but ElevatorMechanism2d was constructed with height limits in meters. Verify whether the mechanism expects meters or rotations; if meters, use getPositionMeters() instead.


49-53: SysId timeout of 1000 seconds is unusually long.

This allows tests to run for ~16 minutes. If intentional, consider adding a comment explaining the rationale. If not, a more typical value might be 10-30 seconds.

hardware/subsystems/arm/ArmSubsystem.java (1)

46-50: SysId timeout of 1000 seconds is very long.

Same as ElevatorSubsystem. Consider documenting if intentional, or reducing to a more typical value.

hardware/subsystems/flywheel/SimpleMotorSubsystem.java (1)

103-110: atTargetState() always returns true in voltage control mode.

Line 107-108 immediately returns true for voltage control. This means any voltage target is considered "reached" instantly, which may be intentional but differs from velocity mode behavior. Document this design choice if intentional.

Comment on lines 51 to 60
motor.setPhysicsSimulation(
new SingleJointedArmSimulation(
config.gearbox,
config.gearRatio,
config.massKilograms,
config.lengthMeters,
config.minimumAngle,
config.maximumAngle,
config.shouldSimulateGravity
));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Parameter order swapped: mass and length are reversed.

The SingleJointedArmSimulation constructor expects (gearbox, gearRatio, armLengthMeters, armMassKilograms, ...) but the code passes massKilograms before lengthMeters. This will cause incorrect physics simulation.

🔎 Proposed fix
         motor.setPhysicsSimulation(
                 new SingleJointedArmSimulation(
                         config.gearbox,
                         config.gearRatio,
-                        config.massKilograms,
                         config.lengthMeters,
+                        config.massKilograms,
                         config.minimumAngle,
                         config.maximumAngle,
                         config.shouldSimulateGravity
                 ));

Comment on lines 108 to 113
public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Edge case: zero angleTolerance causes atTargetState() to never return true.

If angleTolerance is Rotation2d.kZero (the default), getDegrees() returns 0, and the comparison difference < 0 is always false. Consider using <= or documenting that tolerance must be non-zero.

Comment on lines 28 to 41
/**
* Maximum velocity of the elevator in meters per second.
*/
public double maximumVelocity = 1;

/**
* Maximum acceleration of the elevator in meters per second squared.
*/
public double maximumAcceleration = 1;

/**
* Maximum jerk of the elevator in meters per second cubed.
*/
public double maximumJerk = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Missing units in JavaDocs for velocity, acceleration, and jerk.

The ElevatorConfiguration specifies units in JavaDocs for position (meters) but omits units for velocity (meters/second), acceleration (meters/second²), and jerk (meters/second³). This inconsistency could lead to confusion. Consider adding units like in the position tolerance field.

public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.linearPosition(Units.Meters.of(getPositionRotations()))
.linearVelocity(Units.MetersPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Velocity also needs unit conversion.

TalonFXSignal.VELOCITY returns rotations per second, but linearVelocity expects meters per second. Apply the same drum radius conversion used in rotationsToMeters.

Comment on lines 10 to 47
public double
gearRatio = 1,
/**
* The moment of inertia of the motor
*/
momentOfInertia = 0.003,
/**
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
/**
* Maximum velocity displayed by the mechanism 2D
*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
*/
velocityTolerance = 1,
/**
* Ramp rate used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDRampRate = 1,
/**
* Step voltage used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Unconventional multi-variable declaration with inline JavaDocs.

While functional, declaring multiple fields in one statement with interleaved JavaDocs is unusual and harder to maintain. Consider separating each field for clarity and consistency with other configuration classes.

* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
public boolean focEnabled = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Missing documentation for focEnabled.

Unlike other fields, focEnabled has no JavaDoc. Add a brief explanation of what FOC (Field-Oriented Control) does and when to enable/disable it.

Comment on lines 71 to 76
public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.angularPosition(Units.Degrees.of(motor.getSignal(TalonFXSignal.POSITION)))
.angularVelocity(Units.RotationsPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
.voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Unit mismatch: position is in rotations, not degrees.

TalonFXSignal.POSITION returns rotations, but line 73 wraps it with Units.Degrees.of(). Should be Units.Rotations.of().

🔎 Proposed fix
     public void updateLog(SysIdRoutineLog log) {
         log.motor(name)
-                .angularPosition(Units.Degrees.of(motor.getSignal(TalonFXSignal.POSITION)))
+                .angularPosition(Units.Rotations.of(motor.getSignal(TalonFXSignal.POSITION)))
                 .angularVelocity(Units.RotationsPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
                 .voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.angularPosition(Units.Degrees.of(motor.getSignal(TalonFXSignal.POSITION)))
.angularVelocity(Units.RotationsPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
.voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
}
public void updateLog(SysIdRoutineLog log) {
log.motor(name)
.angularPosition(Units.Rotations.of(motor.getSignal(TalonFXSignal.POSITION)))
.angularVelocity(Units.RotationsPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
.voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 28

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7240380 and 850f098.

📒 Files selected for processing (10)
  • subsystems/MotorSubsystem.java
  • subsystems/arm/ArmSubsystem.java
  • subsystems/arm/ArmSubsystemCommands.java
  • subsystems/arm/ArmSubsystemConfiguration.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/elevator/ElevatorSubsystemConfiguration.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystemConfiguration.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: ## Review Style

  • Explain issues clearly, but don't over-explain. Don't write anything unnecessary or repetetive
  • Suggest a solution when helpful
  • No code blocks unless they genuinely add clarity - GitHub already shows the diff
  • Don't describe what the code does - focus on what could be improved

Naming & Readability

  • Variable, function, and class names must be descriptive and intention-revealing
  • No abbreviations unless universally understood (e.g., id, url)
  • Boolean names should read as questions (isValid, hasPermission, canExecute)
  • Functions should be verbs, classes should be nouns
  • Magic numbers and strings should be named constants
  • Code should be self-documenting; comments should explain "why", not "what"

Clean Code Principles

  • Functions should do one thing and be small
  • Too many parameters? Suggest grouping into an object
  • Flag deep nesting - suggest early returns or extraction
  • DRY: flag duplicated logic
  • Functions with non-obvious side effects
  • Single responsibility for classes and modules
  • Proper abstraction levels - no mixing high and low level logic

Logic & Correctness

  • Off-by-one errors, null/undefined handling, race conditions
  • Unhandled edge cases
  • Incorrect boolean logic or conditions
  • Resource leaks, missing cleanup
  • Error handling gaps
  • Performance issues when significant

Code Structure

  • File and folder organization
  • Proper separation of concerns
  • Dependency management and coupling

Files:

  • subsystems/arm/ArmSubsystemConfiguration.java
  • subsystems/arm/ArmSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystemConfiguration.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/elevator/ElevatorSubsystemConfiguration.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/arm/ArmSubsystem.java
  • subsystems/MotorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
🧬 Code graph analysis (5)
subsystems/arm/ArmSubsystemCommands.java (3)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/GearRatioCalculationCommand.java (1)
  • GearRatioCalculationCommand (20-183)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/flywheel/SimpleMotorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/elevator/ElevatorSubsystem.java (5)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/ElevatorSimulation.java (1)
  • ElevatorSimulation (11-84)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/Conversions.java (1)
  • Conversions (8-258)
utilities/mechanisms/ElevatorMechanism2d.java (1)
  • ElevatorMechanism2d (13-84)
subsystems/elevator/ElevatorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/arm/ArmSubsystem.java (5)
hardware/RobotHardwareStats.java (1)
  • RobotHardwareStats (6-78)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SingleJointedArmSimulation.java (1)
  • SingleJointedArmSimulation (12-81)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/mechanisms/SingleJointedArmMechanism2d.java (1)
  • SingleJointedArmMechanism2d (14-87)
🔇 Additional comments (8)
subsystems/elevator/ElevatorSubsystemCommands.java (1)

13-13: Verify runPeriodically=false is intentional.

Setting runPeriodically to false means setTargetState is called only once during initialize(), not continuously during execute(). If the NetworkTables values change during the command, they won't be applied. If this is intentional for one-time debugging updates, consider renaming the method to reflect that (e.g., getOneTimeDebuggingCommand).

subsystems/arm/ArmSubsystemCommands.java (1)

16-16: Verify runPeriodically=false is intentional.

Setting runPeriodically to false means the target state is set only once during initialize(), not continuously during execute(). If NetworkTables values change during the command, they won't be re-applied. If this one-time behavior is intended, consider a more descriptive method name.

subsystems/MotorSubsystem.java (2)

73-78: LGTM!

The periodic() method correctly delegates to updatePeriodically() and conditionally calls updateMechanism(). Making it final ensures consistent lifecycle behavior across all subclasses.


157-170: LGTM!

createSysIDRoutine correctly handles the null config case and constructs the routine with proper voltage conversion and logging callbacks.

subsystems/elevator/ElevatorSubsystem.java (1)

118-127: LGTM!

State checking logic is clean. Uses meters consistently for tolerance comparison.

subsystems/flywheel/SimpleMotorSubsystemCommands.java (1)

19-41: LGTM!

The state command factories are clean and follow a consistent pattern. Using ExecuteEndCommand ensures the motor stops when the command ends.

subsystems/flywheel/SimpleMotorSubsystem.java (1)

114-121: atTargetState returns true immediately for voltage control.

When shouldUseVoltageControl is true, atTargetState() returns true unconditionally (line 119). This means atState() will return true as soon as a target is set, not when the motor actually reaches the target voltage.

If this is intentional (voltage control has no "at target" concept), document it. Otherwise, compare actual vs target voltage.

subsystems/arm/ArmSubsystem.java (1)

179-185: Remove this comment - getPrepareState() is used in ArmSubsystemCommands.java.

The method is called at line 51 of ArmSubsystemCommands.java as part of a standard pattern used consistently across subsystems (flywheel, elevator). No issue exists.

Likely an incorrect or invalid review comment.

Comment on lines +115 to +124
public boolean atState(ArmState targetState) {
return this.targetState == targetState && atTargetState();
}

public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reference equality check for state may not work as intended.

atState uses == to compare this.targetState == targetState. If ArmState implementations are not singletons (e.g., enums), this check will fail even for logically equal states.

Consider using .equals() or documenting that states must be enum constants.

Comment on lines +126 to +128
public Rotation2d getAngle() {
return Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.POSITION) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Inconsistent offset application between getAngle and setTargetAngle.

In getAngle() (line 127), the offset is added when NOT in simulation.
In setTargetAngle() (line 159), the offset is subtracted when NOT in simulation.

This is likely correct (read vs write compensation), but the dual ternary logic is error-prone and hard to verify. Consider extracting the offset logic into named helper methods for clarity.

Comment on lines +18 to +19
subsystem.getName() + "TargetAngleDegrees",
subsystem.getName() + "SpeedScalar"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

NetworkTables keys lack separator - could be ambiguous.

Concatenating subsystem.getName() + "TargetAngleDegrees" creates keys without clear boundaries. Add a separator like "/" to improve clarity:

subsystem.getName() + "/TargetAngleDegrees",
subsystem.getName() + "/SpeedScalar"

);
}

public static Command getGearRatioCalculationCommand(DoubleConsumer runVoltage, double backlashAccountabilityTimeSeconds, ArmSubsystem subsystem) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Parameter name runVoltage is misleading.

The parameter is a DoubleConsumer that applies voltage to the motor, not a voltage value. Name it to reflect its purpose: applyVoltage, voltageController, or setMotorVoltage.

import edu.wpi.first.wpilibj.util.Color;

public class ArmSubsystemConfiguration {
public String name = "";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty name default hinders debugging.

An empty string for name makes logging and NetworkTables keys unclear. Default to "Arm" or require it in a constructor.

Comment on lines +11 to +47
gearRatio = 1,
/**
* The moment of inertia of the motor
*/
momentOfInertia = 0.003,
/**
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
/**
* Maximum velocity displayed by the mechanism 2D
*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
*/
velocityTolerance = 1,
/**
* Ramp rate used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDRampRate = 1,
/**
* Step voltage used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Placeholder defaults risk misconfiguration.

All numeric fields default to 1 (except momentOfInertia), which are clearly placeholder values. If a user forgets to override these, the subsystem will run with nonsensical parameters. Consider either:

  • Using Double.NaN or throwing on uninitialized values to fail fast
  • Documenting prominently that these must be configured
  • Providing sensible defaults based on actual hardware

* If a subsystem doesn't need to ever brake (i.e. shooter, flywheel, etc.), then it should override the {@link #setBrake(boolean)} method and do nothing.
*/
public abstract class MotorSubsystem extends edu.wpi.first.wpilibj2.command.SubsystemBase {
public static boolean IS_BRAKING = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Public mutable static field is a code smell.

IS_BRAKING is public and mutable, which breaks encapsulation. Any code can modify this without going through the proper setAllSubsystemsBrakeAsync flow.

Consider making it private with a getter, or at least private static with package-private accessor.

🔎 Proposed fix
-    public static boolean IS_BRAKING = true;
+    private static boolean isBraking = true;
+
+    public static boolean isBraking() {
+        return isBraking;
+    }

Then update line 35:

-            IS_BRAKING = true;
+            isBraking = true;

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +31 to +37
static {
DISABLED_TRIGGER.onTrue(new InstantCommand(() -> forEach(MotorSubsystem::stop)).ignoringDisable(true));
DISABLED_TRIGGER.onFalse(new InstantCommand(() -> {
setAllSubsystemsBrakeAsync(true);
IS_BRAKING = true;
}).ignoringDisable(true));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Race condition: IS_BRAKING set before async brake completes.

Line 35 sets IS_BRAKING = true immediately, but setAllSubsystemsBrakeAsync runs on a thread pool. The flag will be true before motors actually finish braking.

If external code checks IS_BRAKING to determine motor state, it will get incorrect information.

Comment on lines +41 to +43
public MotorSubsystem() {
REGISTERED_SUBSYSTEMS.add(this);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Subsystems are never removed from REGISTERED_SUBSYSTEMS.

If a subsystem is ever destroyed or recreated (e.g., in tests), the old reference remains in the list, causing memory leaks and duplicate operations in forEach.

Consider providing an unregister() method or using WeakReference.

Comment on lines +148 to +153
public void changeDefaultCommand(Command newDefaultCommand) {
final Command currentDefaultCommand = getDefaultCommand();
if (currentDefaultCommand != null)
currentDefaultCommand.cancel();
setDefaultCommand(newDefaultCommand);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Canceling running default command may cause unexpected behavior.

changeDefaultCommand cancels the current default command before setting a new one. If the command is actively running, this interruption could leave the subsystem in an inconsistent state. The WPILib setDefaultCommand already handles this gracefully.

Consider removing the explicit cancel or documenting why it's necessary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

♻️ Duplicate comments (20)
hardware/subsystems/elevator/ElevatorConfiguration.java (1)

57-57: Add JavaDoc for gearRatio.

Other fields are documented. Briefly explain what this represents (e.g., motor rotations per output rotation, or output rotations per motor rotation).

hardware/subsystems/flywheel/SimpleMotorConfiguration.java (2)

10-47: Separate field declarations for readability.

The multi-variable declaration with interleaved JavaDocs is hard to maintain. Consider separating each field onto its own line, matching the style in ElevatorConfiguration and ArmConfiguration.


48-48: Add JavaDoc for focEnabled.

Explain what FOC (Field-Oriented Control) does and when to enable/disable it.

hardware/subsystems/arm/ArmConfiguration.java (1)

15-28: Specify units for velocity, acceleration, and jerk.

These fields lack units in their JavaDocs. Are they in rotations/second, radians/second, or degrees/second? Add units for clarity.

subsystems/elevator/ElevatorSubsystemCommands.java (1)

15-16: Add separator to NetworkTables keys.

Keys like "ElevatorTargetPositionMeters" can be ambiguous. Use a separator for clarity:

subsystem.getName() + "/TargetPositionMeters",
subsystem.getName() + "/SpeedScalar"
hardware/subsystems/elevator/ElevatorSubsystem.java (1)

82-87: Bug: Unit mismatch in updateLog.

Two issues:

  1. Line 84: getPositionRotations() returns rotations, but linearPosition expects meters. Use getPositionMeters().
  2. Line 85: TalonFXSignal.VELOCITY returns rotations/second, but linearVelocity expects meters/second. Apply drum radius conversion.
🔎 Proposed fix
     public void updateLog(SysIdRoutineLog log) {
         log.motor(name)
-                .linearPosition(Units.Meters.of(getPositionRotations()))
-                .linearVelocity(Units.MetersPerSecond.of(motor.getSignal(TalonFXSignal.VELOCITY)))
+                .linearPosition(Units.Meters.of(getPositionMeters()))
+                .linearVelocity(Units.MetersPerSecond.of(rotationsToMeters(motor.getSignal(TalonFXSignal.VELOCITY))))
                 .voltage(Units.Volts.of(motor.getSignal(TalonFXSignal.MOTOR_VOLTAGE)));
     }
hardware/subsystems/arm/ArmSubsystem.java (2)

51-60: Bug: Parameters massKilograms and lengthMeters are swapped.

Per SingleJointedArmSimulation constructor signature (gearbox, gearRatio, armLengthMeters, armMassKilograms, ...), length should come before mass. Current order passes mass first, causing incorrect physics simulation.

🔎 Proposed fix
         motor.setPhysicsSimulation(
                 new SingleJointedArmSimulation(
                         config.gearbox,
                         config.gearRatio,
-                        config.massKilograms,
                         config.lengthMeters,
+                        config.massKilograms,
                         config.minimumAngle,
                         config.maximumAngle,
                         config.shouldSimulateGravity
                 ));

108-113: Edge case: Zero angleTolerance causes atTargetState() to always return false.

Default angleTolerance is Rotation2d.kZero (0 degrees). The comparison difference < 0 is never true. Use <= or require non-zero tolerance.

🔎 Proposed fix
     public boolean atTargetState() {
         if (targetState == null)
             return false;
         final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
-        return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
+        return currentToTargetStateDifferenceDegrees <= angleTolerance.getDegrees();
     }
subsystems/arm/ArmSubsystemCommands.java (2)

18-19: Add separator to NetworkTables keys.

Use "/" separator for clarity: subsystem.getName() + "/TargetAngleDegrees".


23-23: Rename runVoltage parameter.

The parameter is a DoubleConsumer that applies voltage, not a voltage value. Consider applyVoltage or voltageApplier.

subsystems/arm/ArmSubsystem.java (2)

52-61: Constructor parameter order mismatch with SingleJointedArmSimulation.

Per the SingleJointedArmSimulation constructor signature in hardware/simulation/SingleJointedArmSimulation.java, the parameter order is (gearbox, gearRatio, armLengthMeters, armMassKilograms, ...). You're passing massKilograms before lengthMeters, which will cause incorrect physics simulation.

Swap lines 56-57.


115-117: Reference equality check for state may not work as intended.

atState uses == to compare states. If ArmState implementations are not enums or singletons, this comparison will fail for logically equivalent states. Use .equals() or document that states must be enum constants.

subsystems/elevator/ElevatorSubsystem.java (2)

89-95: Unit mismatch: logging rotations as meters.

getPositionRotations() returns rotations, but you're wrapping it with Units.Meters.of() on line 92. Same issue on line 93 where velocity in rotations/sec is logged as MetersPerSecond. Use getPositionMeters() and rotationsToMeters() for proper conversions.


101-104: Mechanism update uses wrong units.

ElevatorMechanism2d expects meters (based on maximumHeight, minimumHeight constructor params), but getPositionRotations() and CLOSED_LOOP_REFERENCE are in rotations. Convert both to meters.

subsystems/flywheel/SimpleMotorSubsystemCommands.java (1)

10-17: Debugging command runs only once, not continuously.

runPeriodically is false, so setTargetVelocity is called only in initialize(). For a debugging command that should continuously read NetworkTables values, set this to true.

subsystems/flywheel/SimpleMotorSubsystem.java (3)

19-22: Unused fields: maximumAcceleration and maximumJerk.

These fields are assigned but never used. VelocityVoltage doesn't have acceleration/jerk parameters like DynamicMotionMagicVoltage. Remove them or use a control request that supports these parameters.

Also applies to: 35-37


78-84: Inconsistent units in updateLog.

Line 81 logs position as Units.Degrees.of(motor.getSignal(TalonFXSignal.POSITION)), but POSITION returns rotations, not degrees. Use Units.Rotations.of(...) for consistency with ArmSubsystem.updateLog.


86-96: Potential NullPointerException when targetState is null.

In velocity control mode, line 94 calls targetState.getTargetUnit() without a null check. If updateMechanism() is invoked before any target is set, this throws NPE.

Add a null guard:

     if (shouldUseVoltageControl) {
         mechanism.update(getVoltage());
         return;
     }
+    if (targetState == null) {
+        mechanism.update(getVelocityRotationsPerSecond());
+        return;
+    }
     mechanism.update(
             getVelocityRotationsPerSecond(),
             targetState.getTargetUnit()
     );
hardware/subsystems/flywheel/SimpleMotorSubsystem.java (2)

71-76: Unit mismatch: position in rotations, not degrees.

TalonFXSignal.POSITION returns rotations. Line 73 wraps it with Units.Degrees.of() which is incorrect. Use Units.Rotations.of().


78-87: Potential NullPointerException when targetState is null.

Line 85 accesses targetState.getTargetUnit() without null check in velocity control mode. Add a guard before accessing.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 850f098 and cf14ae5.

📒 Files selected for processing (12)
  • hardware/subsystems/arm/ArmConfiguration.java
  • hardware/subsystems/arm/ArmSubsystem.java
  • hardware/subsystems/elevator/ElevatorConfiguration.java
  • hardware/subsystems/elevator/ElevatorSubsystem.java
  • hardware/subsystems/flywheel/SimpleMotorConfiguration.java
  • hardware/subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/arm/ArmSubsystem.java
  • subsystems/arm/ArmSubsystemCommands.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: ## Review Style

  • Explain issues clearly, but don't over-explain. Don't write anything unnecessary or repetetive
  • Suggest a solution when helpful
  • No code blocks unless they genuinely add clarity - GitHub already shows the diff
  • Don't describe what the code does - focus on what could be improved

Naming & Readability

  • Variable, function, and class names must be descriptive and intention-revealing
  • No abbreviations unless universally understood (e.g., id, url)
  • Boolean names should read as questions (isValid, hasPermission, canExecute)
  • Functions should be verbs, classes should be nouns
  • Magic numbers and strings should be named constants
  • Code should be self-documenting; comments should explain "why", not "what"

Clean Code Principles

  • Functions should do one thing and be small
  • Too many parameters? Suggest grouping into an object
  • Flag deep nesting - suggest early returns or extraction
  • DRY: flag duplicated logic
  • Functions with non-obvious side effects
  • Single responsibility for classes and modules
  • Proper abstraction levels - no mixing high and low level logic

Logic & Correctness

  • Off-by-one errors, null/undefined handling, race conditions
  • Unhandled edge cases
  • Incorrect boolean logic or conditions
  • Resource leaks, missing cleanup
  • Error handling gaps
  • Performance issues when significant

Code Structure

  • File and folder organization
  • Proper separation of concerns
  • Dependency management and coupling

Files:

  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/arm/ArmSubsystem.java
  • hardware/subsystems/arm/ArmSubsystem.java
  • hardware/subsystems/arm/ArmConfiguration.java
  • hardware/subsystems/elevator/ElevatorSubsystem.java
  • subsystems/arm/ArmSubsystemCommands.java
  • hardware/subsystems/flywheel/SimpleMotorConfiguration.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/elevator/ElevatorSubsystem.java
  • hardware/subsystems/flywheel/SimpleMotorSubsystem.java
  • hardware/subsystems/elevator/ElevatorConfiguration.java
🧬 Code graph analysis (7)
subsystems/flywheel/SimpleMotorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/elevator/ElevatorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/arm/ArmSubsystem.java (5)
hardware/RobotHardwareStats.java (1)
  • RobotHardwareStats (6-78)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SingleJointedArmSimulation.java (1)
  • SingleJointedArmSimulation (12-81)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/mechanisms/SingleJointedArmMechanism2d.java (1)
  • SingleJointedArmMechanism2d (14-87)
hardware/subsystems/arm/ArmSubsystem.java (4)
hardware/RobotHardwareStats.java (1)
  • RobotHardwareStats (6-78)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SingleJointedArmSimulation.java (1)
  • SingleJointedArmSimulation (12-81)
utilities/mechanisms/SingleJointedArmMechanism2d.java (1)
  • SingleJointedArmMechanism2d (14-87)
subsystems/arm/ArmSubsystemCommands.java (3)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/GearRatioCalculationCommand.java (1)
  • GearRatioCalculationCommand (20-183)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/flywheel/SimpleMotorSubsystem.java (4)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SimpleMotorSimulation.java (1)
  • SimpleMotorSimulation (12-81)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/mechanisms/SpeedMechanism2d.java (1)
  • SpeedMechanism2d (12-129)
hardware/subsystems/flywheel/SimpleMotorSubsystem.java (3)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SimpleMotorSimulation.java (1)
  • SimpleMotorSimulation (12-81)
utilities/mechanisms/SpeedMechanism2d.java (1)
  • SpeedMechanism2d (12-129)
🔇 Additional comments (4)
subsystems/arm/ArmSubsystem.java (1)

174-185: LGTM - ArmState interface is well-defined.

The interface provides a clear contract for arm states with target angle, prepare state, and speed scalar. This pattern enables polymorphic state handling.

subsystems/elevator/ElevatorSubsystem.java (1)

37-67: LGTM - Constructor properly initializes all subsystem components.

Physics simulation, mechanism visualization, control requests, and SysId config are all correctly wired.

subsystems/flywheel/SimpleMotorSubsystemCommands.java (1)

19-41: LGTM - Target state commands correctly use ExecuteEndCommand.

The pattern of setting target in execute and calling stop on end is appropriate for these commands.

hardware/subsystems/flywheel/SimpleMotorSubsystem.java (1)

42-46: : The code at lines 43-46 already uses null for the timeout parameter, not Units.Second.of(1000). This is consistent with other similar subsystems (ElevatorSubsystem, ArmSubsystem). No action needed.

Likely an incorrect or invalid review comment.

*/
public double sysIDStepVoltage = 1;

public double gearRatio = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add JavaDoc for gearRatio.

Document what this represents (e.g., motor rotations per arm rotation).

Comment on lines 71 to 73
public boolean focEnabled = true;

public boolean shouldSimulateGravity = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add JavaDocs for focEnabled and shouldSimulateGravity.

These boolean flags lack documentation explaining their purpose.

Comment on lines 160 to 164
public interface ArmState {
Rotation2d getTargetAngle();

double getSpeedScalar();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add getPrepareState() to ArmState interface.

ArmSubsystemCommands.getPrepareTargetStateCommand() calls targetState.getPrepareState(), but this method isn't declared in the interface.

🔎 Proposed fix
     public interface ArmState {
         Rotation2d getTargetAngle();

         double getSpeedScalar();
+
+        ArmState getPrepareState();
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public interface ArmState {
Rotation2d getTargetAngle();
double getSpeedScalar();
}
public interface ArmState {
Rotation2d getTargetAngle();
double getSpeedScalar();
ArmState getPrepareState();
}

Comment on lines 63 to 65
public boolean focEnabled = true;

public boolean shouldSimulateGravity = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Missing documentation for focEnabled and shouldSimulateGravity.

Add brief JavaDocs explaining what FOC (Field-Oriented Control) does and when gravity simulation should be enabled/disabled.

Comment on lines 89 to 96
public void updateMechanism() {
logComponentPoses();

mechanism.update(
getPositionRotations(),
motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE)
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.java" | xargs grep -l "class ElevatorMechanism2d"

Repository: Programming-TRIGON/TRIGONLib

Length of output: 119


🏁 Script executed:

cat -n ./utilities/mechanisms/ElevatorMechanism2d.java | head -150

Repository: Programming-TRIGON/TRIGONLib

Length of output: 4051


🏁 Script executed:

cat -n ./hardware/subsystems/elevator/ElevatorSubsystem.java | head -120

Repository: Programming-TRIGON/TRIGONLib

Length of output: 157


🏁 Script executed:

find . -type f -name "ElevatorSubsystem.java"

Repository: Programming-TRIGON/TRIGONLib

Length of output: 116


🏁 Script executed:

cat -n ./subsystems/elevator/ElevatorSubsystem.java | sed -n '80,105p'

Repository: Programming-TRIGON/TRIGONLib

Length of output: 992


🏁 Script executed:

grep -n "getPositionRotations" ./subsystems/elevator/ElevatorSubsystem.java

Repository: Programming-TRIGON/TRIGONLib

Length of output: 542


🏁 Script executed:

cat -n ./subsystems/elevator/ElevatorSubsystem.java | sed -n '129,170p'

Repository: Programming-TRIGON/TRIGONLib

Length of output: 1972


🏁 Script executed:

grep -n "mechanism.update" ./subsystems/elevator/ElevatorSubsystem.java

Repository: Programming-TRIGON/TRIGONLib

Length of output: 101


🏁 Script executed:

cat -n ./subsystems/elevator/ElevatorSubsystem.java | sed -n '1,50p'

Repository: Programming-TRIGON/TRIGONLib

Length of output: 2843


Pass position in meters to mechanism.update().

Line 102 passes getPositionRotations() (in rotations) to mechanism.update(), but the mechanism expects meters. The mechanism displays elevator height and is initialized with maximumHeight and minimumHeight (line 47), then uses position values as-is in setLength() calls (lines 73, 82). Elsewhere in the subsystem, rotations are explicitly converted to meters for display purposes (line 168). Convert the position and target:

mechanism.update(
        getPositionMeters(),
        rotationsToMeters(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE))
);

Comment on lines 17 to 27
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Specify units for velocity, acceleration, and jerk.

The JavaDocs don't indicate what units these values use (rotations/second? radians/second?). Add units for consistency with other configuration classes.

Comment on lines 150 to 157
/**
* An interface for a Simple motor state.
* getTargetUnit represents the target Velocity or Voltage (depending on the control mode) of the state.
*/
public interface SimpleMotorState {
double getTargetUnit();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

SimpleMotorState interface missing getPrepareState() method.

The SimpleMotorState interface in subsystems/flywheel/SimpleMotorSubsystem.java includes getPrepareState(), but this version doesn't. This creates an API inconsistency. If both classes are meant to be interchangeable, align the interfaces.

Comment on lines +118 to +120
public boolean atState(ElevatorState targetState) {
return this.targetState == targetState && atTargetState();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reference equality check for state may fail for non-singleton implementations.

Same issue as ArmSubsystem.atState() - using == instead of .equals(). If ElevatorState implementations aren't enums, logically equal states won't match.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf14ae5 and 6f6c440.

📒 Files selected for processing (9)
  • subsystems/arm/ArmSubsystem.java
  • subsystems/arm/ArmSubsystemCommands.java
  • subsystems/arm/ArmSubsystemConfiguration.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/elevator/ElevatorSubsystemConfiguration.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystemConfiguration.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: ## Review Style

  • Explain issues clearly, but don't over-explain. Don't write anything unnecessary or repetetive
  • Suggest a solution when helpful
  • No code blocks unless they genuinely add clarity - GitHub already shows the diff
  • Don't describe what the code does - focus on what could be improved

Naming & Readability

  • Variable, function, and class names must be descriptive and intention-revealing
  • No abbreviations unless universally understood (e.g., id, url)
  • Boolean names should read as questions (isValid, hasPermission, canExecute)
  • Functions should be verbs, classes should be nouns
  • Magic numbers and strings should be named constants
  • Code should be self-documenting; comments should explain "why", not "what"

Clean Code Principles

  • Functions should do one thing and be small
  • Too many parameters? Suggest grouping into an object
  • Flag deep nesting - suggest early returns or extraction
  • DRY: flag duplicated logic
  • Functions with non-obvious side effects
  • Single responsibility for classes and modules
  • Proper abstraction levels - no mixing high and low level logic

Logic & Correctness

  • Off-by-one errors, null/undefined handling, race conditions
  • Unhandled edge cases
  • Incorrect boolean logic or conditions
  • Resource leaks, missing cleanup
  • Error handling gaps
  • Performance issues when significant

Code Structure

  • File and folder organization
  • Proper separation of concerns
  • Dependency management and coupling

Files:

  • subsystems/arm/ArmSubsystemCommands.java
  • subsystems/elevator/ElevatorSubsystemConfiguration.java
  • subsystems/arm/ArmSubsystemConfiguration.java
  • subsystems/elevator/ElevatorSubsystemCommands.java
  • subsystems/arm/ArmSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystemCommands.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystemConfiguration.java
🧬 Code graph analysis (5)
subsystems/arm/ArmSubsystemCommands.java (3)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/GearRatioCalculationCommand.java (1)
  • GearRatioCalculationCommand (20-183)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/elevator/ElevatorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/flywheel/SimpleMotorSubsystemCommands.java (2)
commands/ExecuteEndCommand.java (1)
  • ExecuteEndCommand (9-21)
commands/NetworkTablesCommand.java (1)
  • NetworkTablesCommand (16-139)
subsystems/flywheel/SimpleMotorSubsystem.java (4)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/SimpleMotorSimulation.java (1)
  • SimpleMotorSimulation (12-81)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/mechanisms/SpeedMechanism2d.java (1)
  • SpeedMechanism2d (12-129)
subsystems/elevator/ElevatorSubsystem.java (5)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/ElevatorSimulation.java (1)
  • ElevatorSimulation (11-84)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/Conversions.java (1)
  • Conversions (8-258)
utilities/mechanisms/ElevatorMechanism2d.java (1)
  • ElevatorMechanism2d (13-84)
🔇 Additional comments (6)
subsystems/elevator/ElevatorSubsystem.java (2)

118-127: LGTM!

State comparison and tolerance checking logic is correct. Using reference equality (==) for targetState is intentional for enum-like state comparisons.


166-172: Verify stage indexing doesn't divide by zero.

targetStage + 1 is used as a divisor. Since the loop starts at i = 0, targetStage + 1 is always at least 1, so this is safe. However, the division logic getPositionMeters() / (targetStage + 1) for multi-stage elevators may not produce expected results—stage 0 gets full position, stage 1 gets half, etc. Confirm this matches the intended kinematic model.

subsystems/arm/ArmSubsystemConfiguration.java (1)

7-81: LGTM overall—configuration data class is straightforward.

Public mutable fields are typical for FRC configuration classes. The defaults are documented where non-obvious. Consider adding brief Javadoc for gearRatio and focEnabled to match the style of other fields.

subsystems/arm/ArmSubsystemCommands.java (1)

33-47: LGTM—state commands are well-structured.

Clean use of ExecuteEndCommand with proper stop behavior on end. The overloaded methods provide good flexibility.

subsystems/arm/ArmSubsystem.java (2)

34-64: Constructor is well-organized.

Good separation of configuration extraction, request initialization, simulation setup, and mechanism creation. The physics simulation wiring with SingleJointedArmSimulation looks correct.


86-92: Logging and update methods look correct.

Proper use of AdvantageKit logging with appropriate signals. The updatePeriodically method correctly updates motor state before logging.

Also applies to: 104-108

Comment on lines +115 to +117
public boolean atState(ArmState targetState) {
return this.targetState == targetState && atTargetState();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reference equality (==) for state comparison may fail for non-enum states.

Using == works if ArmState is always an enum, but fails for other implementations. If the interface is intended for enums only, document it. Otherwise, use .equals() or compare by value.

Comment on lines +119 to +124
public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Strict < comparison with tolerance may cause false negatives.

Using < instead of <= means being exactly at the tolerance boundary returns false. Combined with angleTolerance defaulting to zero, atTargetState() is effectively always false unless tolerance is explicitly set.

Suggested fix
-        return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
+        return currentToTargetStateDifferenceDegrees <= angleTolerance.getDegrees();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees < angleTolerance.getDegrees();
}
public boolean atTargetState() {
if (targetState == null)
return false;
final double currentToTargetStateDifferenceDegrees = Math.abs(targetState.getTargetAngle().minus(getAngle()).getDegrees());
return currentToTargetStateDifferenceDegrees <= angleTolerance.getDegrees();
}

Comment on lines +152 to +156
private void scalePositionRequestSpeed(double speedScalar) {
positionRequest.Velocity = maximumVelocity * speedScalar;
positionRequest.Acceleration = maximumAcceleration * speedScalar;
positionRequest.Jerk = maximumJerk * speedScalar;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Directly mutating positionRequest fields is not thread-safe.

While likely fine in single-threaded robot code, mutating shared request objects could cause issues if methods are called from different contexts (e.g., command scheduling). Consider creating a new request or documenting the threading assumptions.

Comment on lines +174 to +185
/**
* An interface for an arm state.
* getTargetAngle represents the target angle of the state.
* getSpeedScalar represents the speed scalar of the state.
*/
public interface ArmState {
Rotation2d getTargetAngle();

ArmState getPrepareState();

double getSpeedScalar();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Interface design is clean.

The ArmState interface is minimal and well-documented. Consider whether getPrepareState() should have a default implementation returning this to avoid null issues.

Comment on lines +13 to +21
public static Command getDebuggingCommand(ArmSubsystem subsystem) {
return new NetworkTablesCommand(
(targetAngleDegrees, speedScalar) -> subsystem.setTargetState(Rotation2d.fromDegrees(targetAngleDegrees), speedScalar),
false,
Set.of(subsystem),
subsystem.getName() + "TargetAngleDegrees",
subsystem.getName() + "SpeedScalar"
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

runPeriodically=false seems wrong for a debugging command.

With runPeriodically=false, the command reads NetworkTables values only once on initialize(), ignoring any subsequent changes. For debugging/tuning, you'd typically want continuous updates.

Suggested fix
         return new NetworkTablesCommand(
                 (targetAngleDegrees, speedScalar) -> subsystem.setTargetState(Rotation2d.fromDegrees(targetAngleDegrees), speedScalar),
-                false,
+                true,
                 Set.of(subsystem),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static Command getDebuggingCommand(ArmSubsystem subsystem) {
return new NetworkTablesCommand(
(targetAngleDegrees, speedScalar) -> subsystem.setTargetState(Rotation2d.fromDegrees(targetAngleDegrees), speedScalar),
false,
Set.of(subsystem),
subsystem.getName() + "TargetAngleDegrees",
subsystem.getName() + "SpeedScalar"
);
}
public static Command getDebuggingCommand(ArmSubsystem subsystem) {
return new NetworkTablesCommand(
(targetAngleDegrees, speedScalar) -> subsystem.setTargetState(Rotation2d.fromDegrees(targetAngleDegrees), speedScalar),
true,
Set.of(subsystem),
subsystem.getName() + "TargetAngleDegrees",
subsystem.getName() + "SpeedScalar"
);
}


import java.util.Set;

public class SimpleMotorSubsystemCommands {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Make this a proper utility class.

Since this class contains only static methods, make it final and add a private constructor to prevent instantiation.

Comment on lines +35 to +41
public static Command getPrepareTargetStateCommand(SimpleMotorSubsystem.SimpleMotorState targetState, SimpleMotorSubsystem subsystem) {
return new ExecuteEndCommand(
() -> subsystem.setTargetState(targetState.getPrepareState()),
subsystem::stop,
subsystem
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.java" | head -20

Repository: Programming-TRIGON/TRIGONLib

Length of output: 885


🏁 Script executed:

fd -e java | xargs grep -l "SimpleMotorState"

Repository: Programming-TRIGON/TRIGONLib

Length of output: 171


🏁 Script executed:

fd -e java | xargs grep -l "class SimpleMotorSubsystem"

Repository: Programming-TRIGON/TRIGONLib

Length of output: 230


🏁 Script executed:

fd -e java -path "*flywheel*" | head -20

Repository: Programming-TRIGON/TRIGONLib

Length of output: 240


🏁 Script executed:

cat -n subsystems/flywheel/SimpleMotorSubsystem.java

Repository: Programming-TRIGON/TRIGONLib

Length of output: 7258


🏁 Script executed:

cat -n subsystems/flywheel/SimpleMotorSubsystemCommands.java

Repository: Programming-TRIGON/TRIGONLib

Length of output: 1917


Add null check for getPrepareState() result.

If targetState.getPrepareState() returns null, setTargetState(null) will throw NPE when it calls getTargetUnit(). The interface doesn't guarantee non-null, and other code in the subsystem handles null targetState (e.g., atTargetState(), updateMechanism()). Either:

  • Add a null check here: SimpleMotorState prepareState = targetState.getPrepareState(); if (prepareState != null) subsystem.setTargetState(prepareState);
  • Or document that getPrepareState() never returns null with @Nonnull

Comment on lines +8 to +56
public class SimpleMotorSubsystemConfiguration {
public String name = "";
public double
gearRatio = 1,
/**
* The moment of inertia of the motor
*/
momentOfInertia = 0.003,
/**
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
/**
* Maximum velocity displayed by the mechanism 2D
*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
*/
velocityTolerance = 1,
/**
* Ramp rate used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDRampRate = 1,
/**
* Step voltage used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
public boolean focEnabled = true;
/**
* should the motor control mode be Voltage as opposed to Velocity.
*/
public boolean shouldUseVoltageControl = false;
/**
* The type and amount of motors to be used in the simulation.
*/
public DCMotor gearbox = DCMotor.getKrakenX60Foc(1);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Encapsulate configuration fields.

Public mutable fields prevent validation and make it easy to create invalid configurations. Consider making fields final with a builder pattern, or use a constructor that validates inputs.

Comment on lines +9 to +47
public String name = "";
public double
gearRatio = 1,
/**
* The moment of inertia of the motor
*/
momentOfInertia = 0.003,
/**
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
/**
* Maximum velocity displayed by the mechanism 2D
*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
*/
velocityTolerance = 1,
/**
* Ramp rate used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDRampRate = 1,
/**
* Step voltage used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Questionable default values.

Several defaults appear arbitrary:

  • Empty string for name makes debugging difficult
  • Value of 1 for physical parameters (velocity, acceleration, jerk, tolerance, etc.) is unlikely to be correct for real hardware

Either require these values in a constructor or provide physically meaningful defaults with documentation explaining the assumptions.

Comment on lines +10 to +47
public double
gearRatio = 1,
/**
* The moment of inertia of the motor
*/
momentOfInertia = 0.003,
/**
* Maximum velocity of the motor.
*/
maximumVelocity = 1,
/**
* Maximum acceleration of the motor.
*/
maximumAcceleration = 1,
/**
* Maximum jerk of the motor.
*/
maximumJerk = 1,
/**
* Maximum velocity displayed by the mechanism 2D
*/
maximumDisplayableVelocity = 1,
/**
* The acceptable tolerance between the target velocity and current velocity.
*/
velocityTolerance = 1,
/**
* Ramp rate used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDRampRate = 1,
/**
* Step voltage used in system identification.
* See <a href="https://docs.wpilib.org/en/stable/docs/software/advanced-controls/system-identification/introduction.html">
* WPILib System Identification Introduction</a>.
*/
sysIDStepVoltage = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Separate field declarations for readability.

Declaring multiple fields in a single compound statement with interspersed Javadoc makes the code harder to read and maintain. Declare each field separately with its own Javadoc comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f6c440 and 6b9824d.

📒 Files selected for processing (3)
  • subsystems/arm/ArmSubsystem.java
  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

**/*: ## Review Style

  • Explain issues clearly, but don't over-explain. Don't write anything unnecessary or repetetive
  • Suggest a solution when helpful
  • No code blocks unless they genuinely add clarity - GitHub already shows the diff
  • Don't describe what the code does - focus on what could be improved

Naming & Readability

  • Variable, function, and class names must be descriptive and intention-revealing
  • No abbreviations unless universally understood (e.g., id, url)
  • Boolean names should read as questions (isValid, hasPermission, canExecute)
  • Functions should be verbs, classes should be nouns
  • Magic numbers and strings should be named constants
  • Code should be self-documenting; comments should explain "why", not "what"

Clean Code Principles

  • Functions should do one thing and be small
  • Too many parameters? Suggest grouping into an object
  • Flag deep nesting - suggest early returns or extraction
  • DRY: flag duplicated logic
  • Functions with non-obvious side effects
  • Single responsibility for classes and modules
  • Proper abstraction levels - no mixing high and low level logic

Logic & Correctness

  • Off-by-one errors, null/undefined handling, race conditions
  • Unhandled edge cases
  • Incorrect boolean logic or conditions
  • Resource leaks, missing cleanup
  • Error handling gaps
  • Performance issues when significant

Code Structure

  • File and folder organization
  • Proper separation of concerns
  • Dependency management and coupling

Files:

  • subsystems/elevator/ElevatorSubsystem.java
  • subsystems/flywheel/SimpleMotorSubsystem.java
  • subsystems/arm/ArmSubsystem.java
🧬 Code graph analysis (2)
subsystems/elevator/ElevatorSubsystem.java (5)
hardware/phoenix6/talonfx/TalonFXMotor.java (1)
  • TalonFXMotor (20-209)
hardware/simulation/ElevatorSimulation.java (1)
  • ElevatorSimulation (11-84)
subsystems/MotorSubsystem.java (1)
  • MotorSubsystem (24-171)
utilities/Conversions.java (1)
  • Conversions (8-258)
utilities/mechanisms/ElevatorMechanism2d.java (1)
  • ElevatorMechanism2d (13-84)
subsystems/arm/ArmSubsystem.java (3)
hardware/RobotHardwareStats.java (1)
  • RobotHardwareStats (6-78)
hardware/simulation/SingleJointedArmSimulation.java (1)
  • SingleJointedArmSimulation (12-81)
utilities/mechanisms/SingleJointedArmMechanism2d.java (1)
  • SingleJointedArmMechanism2d (14-87)
🔇 Additional comments (1)
subsystems/flywheel/SimpleMotorSubsystem.java (1)

116-123: Verify voltage control mode always returns true at target.

In voltage control mode (lines 120-121), atTargetState() immediately returns true without checking if the motor has reached any particular state. Confirm this is intentional—it means commands waiting on atTargetState() will complete instantly in voltage mode.

Comment on lines +52 to +61
motor.setPhysicsSimulation(
new SingleJointedArmSimulation(
config.gearbox,
config.gearRatio,
config.massKilograms,
config.lengthMeters,
config.minimumAngle,
config.maximumAngle,
config.shouldSimulateGravity
));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Arguments swapped in simulation constructor.

Lines 56-57 pass config.massKilograms and config.lengthMeters in the wrong order. The SingleJointedArmSimulation constructor expects armLengthMeters as the 3rd parameter and armMassKilograms as the 4th, but you're passing mass before length. This will break the physics simulation.

🐛 Fix the argument order
         motor.setPhysicsSimulation(
                 new SingleJointedArmSimulation(
                         config.gearbox,
                         config.gearRatio,
-                        config.massKilograms,
                         config.lengthMeters,
+                        config.massKilograms,
                         config.minimumAngle,
                         config.maximumAngle,
                         config.shouldSimulateGravity
                 ));

Comment on lines +66 to +69
@Override
public String getName() {
return "";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: getName() returns empty string instead of name.

This method should return the name field, not an empty string. The current implementation breaks the naming contract and makes the subsystem name inaccessible.

🐛 Return the name field
     @Override
     public String getName() {
-        return "";
+        return name;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public String getName() {
return "";
}
@Override
public String getName() {
return name;
}

Comment on lines +71 to +76
@Override
public SysIdRoutine.Config getSysIDConfig() {
if (sysIDConfig != null)
return sysIDConfig;
return new SysIdRoutine.Config();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove unnecessary null check.

The sysIDConfig field is final and always initialized in the constructor, so the null check is unnecessary. This simplifies the method.

♻️ Simplify the method
     @Override
     public SysIdRoutine.Config getSysIDConfig() {
-        if (sysIDConfig != null)
-            return sysIDConfig;
-        return new SysIdRoutine.Config();
+        return sysIDConfig;
     }


mechanism.update(
getAngle(),
Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Extract duplicated offset calculation.

The expression RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset appears three times (lines 102, 129, 161). Extract it to a private helper method to reduce duplication and improve maintainability.

♻️ Extract to helper method

Add a helper method:

private double getVisualizationOffset() {
    return RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset;
}

Then update the three call sites:

-                Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset))
+                Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE) + getVisualizationOffset())
-        return Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.POSITION) + (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset));
+        return Rotation2d.fromRotations(motor.getSignal(TalonFXSignal.POSITION) + getVisualizationOffset());
-        setControl(positionRequest.withPosition(targetAngle.getRotations() - (RobotHardwareStats.isSimulation() ? 0 : visualizationOffsetFromGravityOffset)));
+        setControl(positionRequest.withPosition(targetAngle.getRotations() - getVisualizationOffset()));

Also applies to: 129-129, 161-161

Comment on lines +37 to +40
public ElevatorSubsystem(TalonFXMotor motor, ElevatorSubsystemConfiguration config, Pose3d... stagesOriginPoints) {
this.motor = motor;
this.stagesOriginPoints = stagesOriginPoints;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing null check on stagesOriginPoints parameter.

The constructor accepts stagesOriginPoints without validation. If null is passed, logComponentPoses() at line 164 will throw NPE when accessing .length.

🛡️ Add validation in constructor
 public ElevatorSubsystem(TalonFXMotor motor, ElevatorSubsystemConfiguration config, Pose3d... stagesOriginPoints) {
+    if (stagesOriginPoints == null)
+        throw new IllegalArgumentException("stagesOriginPoints cannot be null");
     this.motor = motor;
     this.stagesOriginPoints = stagesOriginPoints;

Alternatively, default to an empty array:

 public ElevatorSubsystem(TalonFXMotor motor, ElevatorSubsystemConfiguration config, Pose3d... stagesOriginPoints) {
     this.motor = motor;
-    this.stagesOriginPoints = stagesOriginPoints;
+    this.stagesOriginPoints = stagesOriginPoints != null ? stagesOriginPoints : new Pose3d[0];

Comment on lines +99 to +107
@Override
public void updateMechanism() {
logComponentPoses();

mechanism.update(
getPositionRotations(),
motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE)
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Unit mismatch: mechanism.update() expects meters for both parameters.

Lines 104-105 pass rotations to mechanism.update(), but ElevatorMechanism2d expects both current and target position in meters (see setCurrentPosition in ElevatorMechanism2d.java line 73).

🐛 Fix: Convert both positions to meters
 @Override
 public void updateMechanism() {
     logComponentPoses();
 
     mechanism.update(
-            getPositionRotations(),
-            motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE)
+            getPositionMeters(),
+            rotationsToMeters(motor.getSignal(TalonFXSignal.CLOSED_LOOP_REFERENCE))
     );
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +19 to +22
private final double
maximumVelocity,
maximumAcceleration,
maximumJerk;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused fields.

The fields maximumVelocity, maximumAcceleration, and maximumJerk are initialized from config but never used. Remove them to reduce clutter.

Comment on lines +58 to +61
@Override
public String getName() {
return "";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Return the actual name instead of empty string.

Line 55 calls setName(name), but this override returns "". Return the name field instead.

🐛 Proposed fix
 @Override
 public String getName() {
-    return "";
+    return name;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public String getName() {
return "";
}
@Override
public String getName() {
return name;
}

Comment on lines +63 to +68
@Override
public SysIdRoutine.Config getSysIDConfig() {
if (sysIDConfig != null)
return sysIDConfig;
return new SysIdRoutine.Config();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Remove unnecessary null check.

sysIDConfig is always initialized in the constructor and never set to null. The null check on line 65 is unreachable.

♻️ Simplify
 @Override
 public SysIdRoutine.Config getSysIDConfig() {
-    if (sysIDConfig != null)
-        return sysIDConfig;
-    return new SysIdRoutine.Config();
+    return sysIDConfig;
 }

Comment on lines +167 to +171
public interface SimpleMotorState {
double getTargetUnit();

SimpleMotorState getPrepareState();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Document getPrepareState() purpose.

The getPrepareState() method in the public interface has no documentation explaining when or why it's used. Add a javadoc comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants