From cdcbe99f2ccca5696bc4e97d54db1cc4e5e2df9e Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 4 Aug 2019 16:21:56 -0600 Subject: [PATCH 01/70] Rename XBoxTriggerButton.java to XboxTriggerButton.java --- .../{XBoxTriggerButton.java => XboxTriggerButton.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/frc4388/controller/{XBoxTriggerButton.java => XboxTriggerButton.java} (99%) diff --git a/src/main/java/frc4388/controller/XBoxTriggerButton.java b/src/main/java/frc4388/controller/XboxTriggerButton.java similarity index 99% rename from src/main/java/frc4388/controller/XBoxTriggerButton.java rename to src/main/java/frc4388/controller/XboxTriggerButton.java index a922fec..ee5b957 100644 --- a/src/main/java/frc4388/controller/XBoxTriggerButton.java +++ b/src/main/java/frc4388/controller/XboxTriggerButton.java @@ -67,4 +67,4 @@ else if (m_trigger == LEFT_AXIS_LEFT_TRIGGER) { } return false; } -} \ No newline at end of file +} From 10346925c66f26daab7627a7c55ae4067337c790 Mon Sep 17 00:00:00 2001 From: HFocus Date: Wed, 7 Aug 2019 18:46:31 -0600 Subject: [PATCH 02/70] Formating and Conventions Work - Moved controller code to utilities - added m_ prefix to member variables to stick with conventions --- src/main/java/frc4388/robot/Robot.java | 3 ++- src/main/java/frc4388/robot/commands/setLEDPattern.java | 8 ++++---- src/main/java/frc4388/robot/constants/LEDPatterns.java | 8 ++++++-- .../frc4388/{ => utility}/controller/IHandController.java | 2 +- .../frc4388/{ => utility}/controller/XboxController.java | 2 +- .../{ => utility}/controller/XboxTriggerButton.java | 4 ++-- 6 files changed, 16 insertions(+), 11 deletions(-) rename src/main/java/frc4388/{ => utility}/controller/IHandController.java (88%) rename src/main/java/frc4388/{ => utility}/controller/XboxController.java (99%) rename src/main/java/frc4388/{ => utility}/controller/XboxTriggerButton.java (96%) diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index 1ee2f5e..ed3e072 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -15,6 +15,7 @@ import frc4388.robot.commands.ExampleCommand; import frc4388.robot.subsystems.ExampleSubsystem; import frc4388.robot.subsystems.LED; +import frc4388.utility.controller.XboxController; /** * The VM is configured to automatically run this class, and to call the @@ -25,7 +26,7 @@ */ public class Robot extends TimedRobot { public static ExampleSubsystem m_subsystem = new ExampleSubsystem(); - public static LED led = new LED(); + public static LED m_led = new LED(); public static OI m_oi; Command m_autonomousCommand; diff --git a/src/main/java/frc4388/robot/commands/setLEDPattern.java b/src/main/java/frc4388/robot/commands/setLEDPattern.java index 4c3d16f..83885cd 100644 --- a/src/main/java/frc4388/robot/commands/setLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/setLEDPattern.java @@ -14,11 +14,11 @@ public class setLEDPattern extends Command { - LEDPatterns pattern; + public static LEDPatterns m_pattern; public setLEDPattern(LEDPatterns pattern) { - requires(Robot.led); - this.pattern = pattern; + requires(Robot.m_led); + m_pattern = pattern; } // Called just before this Command runs the first time @@ -29,7 +29,7 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - Robot.led.setPattern(pattern); + Robot.m_led.setPattern(m_pattern); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/constants/LEDPatterns.java b/src/main/java/frc4388/robot/constants/LEDPatterns.java index 16dde54..dc18958 100644 --- a/src/main/java/frc4388/robot/constants/LEDPatterns.java +++ b/src/main/java/frc4388/robot/constants/LEDPatterns.java @@ -33,6 +33,10 @@ public enum LEDPatterns { // GETTERS/SETTERS private final float id; - LEDPatterns(float id) { this.id = id; } - public float getValue() { return id; } + LEDPatterns(float id) { + this.id = id; + } + public float getValue() { + return id; + } } \ No newline at end of file diff --git a/src/main/java/frc4388/controller/IHandController.java b/src/main/java/frc4388/utility/controller/IHandController.java similarity index 88% rename from src/main/java/frc4388/controller/IHandController.java rename to src/main/java/frc4388/utility/controller/IHandController.java index 5d5ca19..40ba864 100644 --- a/src/main/java/frc4388/controller/IHandController.java +++ b/src/main/java/frc4388/utility/controller/IHandController.java @@ -1,4 +1,4 @@ -package frc4388.controller; +package frc4388.utility.controller; public interface IHandController { diff --git a/src/main/java/frc4388/controller/XboxController.java b/src/main/java/frc4388/utility/controller/XboxController.java similarity index 99% rename from src/main/java/frc4388/controller/XboxController.java rename to src/main/java/frc4388/utility/controller/XboxController.java index bd0582c..8a5c326 100644 --- a/src/main/java/frc4388/controller/XboxController.java +++ b/src/main/java/frc4388/utility/controller/XboxController.java @@ -1,4 +1,4 @@ -package frc4388.controller; +package frc4388.utility.controller; import edu.wpi.first.wpilibj.Joystick; diff --git a/src/main/java/frc4388/controller/XboxTriggerButton.java b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java similarity index 96% rename from src/main/java/frc4388/controller/XboxTriggerButton.java rename to src/main/java/frc4388/utility/controller/XboxTriggerButton.java index ee5b957..aed4436 100644 --- a/src/main/java/frc4388/controller/XboxTriggerButton.java +++ b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java @@ -1,6 +1,6 @@ -package frc4388.controller; +package frc4388.utility.controller; -import frc4388.controller.XboxController; +import frc4388.utility.controller.XboxController; import edu.wpi.first.wpilibj.buttons.Button; From 979d21e0c0e2443e5bd13d08130e7a8cdbd92e81 Mon Sep 17 00:00:00 2001 From: HFocus Date: Wed, 7 Aug 2019 18:47:51 -0600 Subject: [PATCH 03/70] Formatting and Conventions Work pt2 --- .../utility/controller/XboxController.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/frc4388/utility/controller/XboxController.java b/src/main/java/frc4388/utility/controller/XboxController.java index 8a5c326..83dcaf4 100644 --- a/src/main/java/frc4388/utility/controller/XboxController.java +++ b/src/main/java/frc4388/utility/controller/XboxController.java @@ -50,14 +50,14 @@ public class XboxController implements IHandController private static final double DEADZONE = 0.1; - private Joystick stick; + private Joystick m_stick; public XboxController(int portNumber){ - stick = new Joystick(portNumber); + m_stick = new Joystick(portNumber); } public Joystick getJoyStick() { - return stick; + return m_stick; } /** @@ -83,67 +83,67 @@ private double getAxisWithDeadZoneCheck(double input){ } public boolean getAButton(){ - return stick.getRawButton(A_BUTTON); + return m_stick.getRawButton(A_BUTTON); } public boolean getXButton(){ - return stick.getRawButton(X_BUTTON); + return m_stick.getRawButton(X_BUTTON); } public boolean getBButton(){ - return stick.getRawButton(B_BUTTON); + return m_stick.getRawButton(B_BUTTON); } public boolean getYButton(){ - return stick.getRawButton(Y_BUTTON); + return m_stick.getRawButton(Y_BUTTON); } public boolean getBackButton(){ - return stick.getRawButton(BACK_BUTTON); + return m_stick.getRawButton(BACK_BUTTON); } public boolean getStartButton(){ - return stick.getRawButton(START_BUTTON); + return m_stick.getRawButton(START_BUTTON); } public boolean getLeftBumperButton(){ - return stick.getRawButton(LEFT_BUMPER_BUTTON); + return m_stick.getRawButton(LEFT_BUMPER_BUTTON); } public boolean getRightBumperButton(){ - return stick.getRawButton(RIGHT_BUMPER_BUTTON); + return m_stick.getRawButton(RIGHT_BUMPER_BUTTON); } public boolean getLeftJoystickButton(){ - return stick.getRawButton(LEFT_JOYSTICK_BUTTON); + return m_stick.getRawButton(LEFT_JOYSTICK_BUTTON); } public boolean getRightJoystickButton(){ - return stick.getRawButton(RIGHT_JOYSTICK_BUTTON); + return m_stick.getRawButton(RIGHT_JOYSTICK_BUTTON); } public double getLeftXAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(LEFT_X_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(LEFT_X_AXIS)); } public double getLeftYAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(LEFT_Y_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(LEFT_Y_AXIS)); } public double getRightXAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(RIGHT_X_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(RIGHT_X_AXIS)); } public double getRightYAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(RIGHT_Y_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(RIGHT_Y_AXIS)); } public double getLeftTriggerAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(LEFT_TRIGGER_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(LEFT_TRIGGER_AXIS)); } public double getRightTriggerAxis(){ - return getAxisWithDeadZoneCheck(stick.getRawAxis(RIGHT_TRIGGER_AXIS)); + return getAxisWithDeadZoneCheck(m_stick.getRawAxis(RIGHT_TRIGGER_AXIS)); } /** @@ -151,23 +151,23 @@ public double getRightTriggerAxis(){ * @return -1 if nothing is pressed, or the angle of the button pressed. 0 = up, 90 = right, etc. */ public int getDpadAngle() { - return stick.getPOV(0); + return m_stick.getPOV(0); } public boolean getDPadLeft(){ - return (stick.getRawAxis(LEFT_RIGHT_DPAD_AXIS) < LEFT_DPAD_TOLERANCE); + return (m_stick.getRawAxis(LEFT_RIGHT_DPAD_AXIS) < LEFT_DPAD_TOLERANCE); } public boolean getDPadRight(){ - return (stick.getRawAxis(LEFT_RIGHT_DPAD_AXIS) > RIGHT_DPAD_TOLERANCE); + return (m_stick.getRawAxis(LEFT_RIGHT_DPAD_AXIS) > RIGHT_DPAD_TOLERANCE); } public boolean getDPadTop(){ - return (stick.getRawAxis(TOP_BOTTOM_DPAD_AXIS) < TOP_DPAD_TOLERANCE); + return (m_stick.getRawAxis(TOP_BOTTOM_DPAD_AXIS) < TOP_DPAD_TOLERANCE); } public boolean getDPadBottom(){ - return (stick.getRawAxis(TOP_BOTTOM_DPAD_AXIS) > BOTTOM_DPAD_TOLERANCE); + return (m_stick.getRawAxis(TOP_BOTTOM_DPAD_AXIS) > BOTTOM_DPAD_TOLERANCE); } public boolean getLeftTrigger(){ From c53e7199162e53269174d028a8890e0a34bf4d92 Mon Sep 17 00:00:00 2001 From: HFocus Date: Wed, 7 Aug 2019 18:48:49 -0600 Subject: [PATCH 04/70] Setup Motors and Controllers --- src/main/java/frc4388/robot/OI.java | 35 ++++++++ src/main/java/frc4388/robot/RobotMap.java | 13 +++ .../java/frc4388/robot/subsystems/Drive.java | 46 ++++++++++ vendordeps/Phoenix.json | 87 +++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 vendordeps/Phoenix.json diff --git a/src/main/java/frc4388/robot/OI.java b/src/main/java/frc4388/robot/OI.java index 9c8ca3e..828c8a2 100644 --- a/src/main/java/frc4388/robot/OI.java +++ b/src/main/java/frc4388/robot/OI.java @@ -7,6 +7,10 @@ package frc4388.robot; +import edu.wpi.first.wpilibj.Joystick; +import frc4388.utility.controller.IHandController; +import frc4388.utility.controller.XboxController; + /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. @@ -39,4 +43,35 @@ public class OI { // Start the command when the button is released and let it run the command // until it is finished as determined by it's isFinished method. // button.whenReleased(new ExampleCommand()); + + private static OI instance; + + private static XboxController m_driverXbox; + private static XboxController m_operatorXbox; + + public OI() { + m_driverXbox = new XboxController(RobotMap.XBOX_DRIVER_ID); + m_operatorXbox = new XboxController(RobotMap.XBOX_OPERATOR_ID); + } + + public static OI getInstance() { + if(instance == null) { + instance = new OI(); + } + return instance; + } + + public IHandController getDriverController() { + return m_driverXbox; + } + + public IHandController getOperatorController() + { + return m_operatorXbox; + } + + public Joystick getOperatorJoystick() + { + return m_operatorXbox.getJoyStick(); + } } diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 7feb605..fe7fe7e 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -7,6 +7,8 @@ package frc4388.robot; +import frc4388.utility.controller.XboxController; + /** * The RobotMap is a mapping from the ports sensors and actuators are wired into * to a variable name. This provides flexibility changing wiring, makes checking @@ -24,5 +26,16 @@ public class RobotMap { // public static int rangefinderPort = 1; // public static int rangefinderModule = 1; + /* Xbox Controllers */ + public static final int XBOX_DRIVER_ID = 0; + public static final int XBOX_OPERATOR_ID = 1; + + /* Blinkin LED Strip */ public static final int LED_SPARK_ID = 0; + + /* Drive Train */ + public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; + public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; + public static final int DRIVE_LEFT_BACK_CAN_ID = 3; + public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; } diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 3627fe9..d6be580 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -7,7 +7,18 @@ package frc4388.robot.subsystems; +import com.ctre.phoenix.motorcontrol.Faults; +import com.ctre.phoenix.motorcontrol.InvertType; +import com.ctre.phoenix.motorcontrol.NeutralMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; + +import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; +import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import frc4388.robot.RobotMap; +import frc4388.robot.Robot; +import frc4388.utility.controller.XboxController; /** * Add your docs here. @@ -16,6 +27,41 @@ public class Drive extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. + WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_FRONT_CAN_ID); + WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_FRONT_CAN_ID); + WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_BACK_CAN_ID); + WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_BACK_CAN_ID); + + DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + + public Drive(){ + /* factory default values */ + m_leftFrontMotor.configFactoryDefault(); + m_rightFrontMotor.configFactoryDefault(); + m_leftBackMotor.configFactoryDefault(); + m_rightBackMotor.configFactoryDefault(); + + /* set back motors as followers */ + m_leftBackMotor.follow(m_leftFrontMotor); + m_rightBackMotor.follow(m_rightFrontMotor); + + /* set neutral mode */ + m_leftFrontMotor.setNeutralMode(NeutralMode.Brake); + m_rightFrontMotor.setNeutralMode(NeutralMode.Brake); + m_leftFrontMotor.setNeutralMode(NeutralMode.Brake); + m_rightFrontMotor.setNeutralMode(NeutralMode.Brake); + + /* flip input so forward becomes back, etc */ + m_leftFrontMotor.setInverted(false); + m_rightFrontMotor.setInverted(false); + m_leftBackMotor.setInverted(InvertType.FollowMaster); + m_rightBackMotor.setInverted(InvertType.FollowMaster); + } + + @Override + public void periodic() { + } + @Override public void initDefaultCommand() { // Set the default command for a subsystem here. diff --git a/vendordeps/Phoenix.json b/vendordeps/Phoenix.json new file mode 100644 index 0000000..d4da1ce --- /dev/null +++ b/vendordeps/Phoenix.json @@ -0,0 +1,87 @@ +{ + "fileName": "Phoenix.json", + "name": "CTRE-Phoenix", + "version": "5.12.0", + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "mavenUrls": [ + "http://devsite.ctr-electronics.com/maven/release/" + ], + "jsonUrl": "http://devsite.ctr-electronics.com/maven/release/com/ctre/phoenix/Phoenix-latest.json", + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.12.0" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.12.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.12.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.12.0", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.12.0", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.12.0", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "core", + "version": "5.12.0", + "libName": "CTRE_PhoenixCore", + "headerClassifier": "headers" + } + ] +} \ No newline at end of file From 5eb1e9b571877d522d5671658a4c94d488ea4186 Mon Sep 17 00:00:00 2001 From: HFocus Date: Wed, 7 Aug 2019 19:20:10 -0600 Subject: [PATCH 05/70] Implement Drive Code --- build.gradle | 2 +- src/main/java/frc4388/robot/Robot.java | 2 ++ .../java/frc4388/robot/subsystems/Drive.java | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 9128ffc..9216a46 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id "edu.wpi.first.GradleRIO" version "2019.1.1" } -def ROBOT_MAIN_CLASS = "frc.robot.Main" +def ROBOT_MAIN_CLASS = "frc4388.robot.Main" // Define my targets (RoboRIO) and artifacts (deployable files) // This is added by GradleRIO's backing project EmbeddedTools. diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index ed3e072..aa44e07 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -13,6 +13,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc4388.robot.commands.ExampleCommand; +import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.ExampleSubsystem; import frc4388.robot.subsystems.LED; import frc4388.utility.controller.XboxController; @@ -27,6 +28,7 @@ public class Robot extends TimedRobot { public static ExampleSubsystem m_subsystem = new ExampleSubsystem(); public static LED m_led = new LED(); + public static Drive m_Drive = new Drive(); public static OI m_oi; Command m_autonomousCommand; diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index d6be580..080c5eb 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -17,6 +17,7 @@ import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc4388.robot.RobotMap; +import frc4388.robot.OI; import frc4388.robot.Robot; import frc4388.utility.controller.XboxController; @@ -27,12 +28,14 @@ public class Drive extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. - WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_FRONT_CAN_ID); - WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_FRONT_CAN_ID); - WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_BACK_CAN_ID); - WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_BACK_CAN_ID); + public static WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_FRONT_CAN_ID); + public static WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_FRONT_CAN_ID); + public static WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_BACK_CAN_ID); + public static WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_BACK_CAN_ID); - DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + public static DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + + private static double m_inputMove, m_inputSteer; public Drive(){ /* factory default values */ @@ -60,6 +63,10 @@ public Drive(){ @Override public void periodic() { + m_inputMove = OI.getInstance().getDriverController().getLeftYAxis(); + m_inputSteer = -(OI.getInstance().getDriverController().getRightXAxis()); + + m_driveTrain.arcadeDrive(m_inputMove, m_inputSteer); } @Override From 7a375558eb17e526166edeec192f6380c0101b10 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Thu, 28 Nov 2019 14:24:12 -0700 Subject: [PATCH 06/70] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9216a46..3e0fc59 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2019.1.1" + id "edu.wpi.first.GradleRIO" version "2020.1.1-beta-2" } def ROBOT_MAIN_CLASS = "frc4388.robot.Main" From ca29b8c4270abaf61aaee94514e04aaa8ca35a50 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Wed, 4 Dec 2019 14:34:07 -0700 Subject: [PATCH 07/70] Add Continous Integration Github service to check if any commit to master or pull request to master is able to build. To be used in pull requests to make sure code will build before a code review. --- .github/workflows/gradle.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/gradle.yml diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..9eaf0d0 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,23 @@ +name: Java CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.12 + - name: Change wrapper permissions + run: chmod +x ./gradlew + - name: Build with Gradle + run: ./gradlew build From 858ce0d8487ee46c90f8f908fb7b6929b312ec11 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 20 Dec 2019 12:53:02 -0700 Subject: [PATCH 08/70] Change Drive to operate on a command based model --- build.gradle | 2 +- src/main/java/frc4388/robot/OI.java | 14 ++++- .../commands/Drive/DriveWithJoystick.java | 53 +++++++++++++++++++ .../robot/commands/Drive/GamerMove.java | 47 ++++++++++++++++ .../java/frc4388/robot/subsystems/Drive.java | 13 +++-- 5 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java create mode 100644 src/main/java/frc4388/robot/commands/Drive/GamerMove.java diff --git a/build.gradle b/build.gradle index 3e0fc59..91e9ef0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2020.1.1-beta-2" + id "edu.wpi.first.GradleRIO" version "2020.1.1-beta-4" } def ROBOT_MAIN_CLASS = "frc4388.robot.Main" diff --git a/src/main/java/frc4388/robot/OI.java b/src/main/java/frc4388/robot/OI.java index 828c8a2..e97b4bb 100644 --- a/src/main/java/frc4388/robot/OI.java +++ b/src/main/java/frc4388/robot/OI.java @@ -8,6 +8,9 @@ package frc4388.robot; import edu.wpi.first.wpilibj.Joystick; +import edu.wpi.first.wpilibj.buttons.JoystickButton; +import frc4388.robot.commands.Drive.DriveWithJoystick; +import frc4388.robot.commands.Drive.GamerMove; import frc4388.utility.controller.IHandController; import frc4388.utility.controller.XboxController; @@ -46,12 +49,16 @@ public class OI { private static OI instance; - private static XboxController m_driverXbox; + public static XboxController m_driverXbox; private static XboxController m_operatorXbox; public OI() { m_driverXbox = new XboxController(RobotMap.XBOX_DRIVER_ID); m_operatorXbox = new XboxController(RobotMap.XBOX_OPERATOR_ID); + + JoystickButton GamerMove = new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON); + GamerMove.whenPressed(new GamerMove()); + GamerMove.whenReleased(new DriveWithJoystick()); } public static OI getInstance() { @@ -74,4 +81,9 @@ public Joystick getOperatorJoystick() { return m_operatorXbox.getJoyStick(); } + + public Joystick getDriverJoystick() + { + return m_driverXbox.getJoyStick(); + } } diff --git a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java new file mode 100644 index 0000000..572b875 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java @@ -0,0 +1,53 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.commands.Drive; + +import edu.wpi.first.wpilibj.command.Command; +import frc4388.robot.OI; +import frc4388.robot.Robot; + +public class DriveWithJoystick extends Command { + + public double m_inputMove, m_inputSteer; + + public DriveWithJoystick() { + // Use requires() here to declare subsystem dependencies + // eg. requires(chassis); + requires(Robot.m_Drive); + } + + // Called just before this Command runs the first time + @Override + protected void initialize() { + } + + // Called repeatedly when this Command is scheduled to run + @Override + protected void execute() { + m_inputMove = OI.getInstance().getDriverController().getLeftYAxis(); + m_inputSteer = -(OI.getInstance().getDriverController().getRightXAxis()); + Robot.m_Drive.driveWithInput(m_inputMove, m_inputSteer); + } + + // Make this return true when this Command no longer needs to run execute() + @Override + protected boolean isFinished() { + return false; + } + + // Called once after isFinished returns true + @Override + protected void end() { + } + + // Called when another command which requires one or more of the same + // subsystems is scheduled to run + @Override + protected void interrupted() { + } +} diff --git a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java new file mode 100644 index 0000000..fbc171c --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java @@ -0,0 +1,47 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.commands.Drive; + +import edu.wpi.first.wpilibj.command.Command; +import frc4388.robot.Robot; + +public class GamerMove extends Command { + public GamerMove() { + // Use requires() here to declare subsystem dependencies + // eg. requires(chassis); + requires(Robot.m_Drive); + } + + // Called just before this Command runs the first time + @Override + protected void initialize() { + } + + // Called repeatedly when this Command is scheduled to run + @Override + protected void execute() { + Robot.m_Drive.gamerMove(); + } + + // Make this return true when this Command no longer needs to run execute() + @Override + protected boolean isFinished() { + return false; + } + + // Called once after isFinished returns true + @Override + protected void end() { + } + + // Called when another command which requires one or more of the same + // subsystems is scheduled to run + @Override + protected void interrupted() { + } +} diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 080c5eb..85cc1f1 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -17,6 +17,8 @@ import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc4388.robot.RobotMap; +import frc4388.robot.commands.Drive.DriveWithJoystick; +import frc4388.robot.commands.Drive.GamerMove; import frc4388.robot.OI; import frc4388.robot.Robot; import frc4388.utility.controller.XboxController; @@ -61,17 +63,18 @@ public Drive(){ m_rightBackMotor.setInverted(InvertType.FollowMaster); } - @Override - public void periodic() { - m_inputMove = OI.getInstance().getDriverController().getLeftYAxis(); - m_inputSteer = -(OI.getInstance().getDriverController().getRightXAxis()); + public void driveWithInput(double move, double steer){ + m_driveTrain.arcadeDrive(move, steer); + } - m_driveTrain.arcadeDrive(m_inputMove, m_inputSteer); + public void gamerMove(){ + m_driveTrain.arcadeDrive(0, 1); } @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); + setDefaultCommand(new DriveWithJoystick()); } } From d36b4784891813ee6daae2ce2a62baa01bfd3f27 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 20 Dec 2019 13:23:51 -0700 Subject: [PATCH 09/70] Update build.gradle Fixes the version so that the code will compile. No idea how to use 2020 version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 91e9ef0..590560a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2020.1.1-beta-4" + id "edu.wpi.first.GradleRIO" version "2019.4.1" } def ROBOT_MAIN_CLASS = "frc4388.robot.Main" From 5dee3b9be1f86436038771e746584091588c92ef Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 21 Dec 2019 20:41:34 -0700 Subject: [PATCH 10/70] LED Command Based --- build.gradle | 2 +- .../robot/commands/Drive/GamerMove.java | 2 +- .../SetLEDPattern.java} | 6 +-- .../frc4388/robot/commands/LED/UpdateLED.java | 46 +++++++++++++++++++ .../java/frc4388/robot/subsystems/Drive.java | 6 +-- .../java/frc4388/robot/subsystems/LED.java | 12 ++++- 6 files changed, 62 insertions(+), 12 deletions(-) rename src/main/java/frc4388/robot/commands/{setLEDPattern.java => LED/SetLEDPattern.java} (91%) create mode 100644 src/main/java/frc4388/robot/commands/LED/UpdateLED.java diff --git a/build.gradle b/build.gradle index 590560a..9216a46 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2019.4.1" + id "edu.wpi.first.GradleRIO" version "2019.1.1" } def ROBOT_MAIN_CLASS = "frc4388.robot.Main" diff --git a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java index fbc171c..b890aa5 100644 --- a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java +++ b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java @@ -25,7 +25,7 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - Robot.m_Drive.gamerMove(); + Robot.m_Drive.driveWithInput(0, 1); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/commands/setLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java similarity index 91% rename from src/main/java/frc4388/robot/commands/setLEDPattern.java rename to src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java index 83885cd..11c4447 100644 --- a/src/main/java/frc4388/robot/commands/setLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java @@ -5,18 +5,18 @@ /* the project. */ /*----------------------------------------------------------------------------*/ -package frc4388.robot.commands; +package frc4388.robot.commands.LED; import frc4388.robot.Robot; import frc4388.robot.constants.LEDPatterns; import edu.wpi.first.wpilibj.command.Command; -public class setLEDPattern extends Command { +public class SetLEDPattern extends Command { public static LEDPatterns m_pattern; - public setLEDPattern(LEDPatterns pattern) { + public SetLEDPattern(LEDPatterns pattern) { requires(Robot.m_led); m_pattern = pattern; } diff --git a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java new file mode 100644 index 0000000..3d6c374 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java @@ -0,0 +1,46 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.commands.LED; + +import edu.wpi.first.wpilibj.command.Command; +import frc4388.robot.Robot; + +public class UpdateLED extends Command { + public UpdateLED() { + // Use requires() here to declare subsystem dependencies + requires(Robot.m_led); + } + + // Called just before this Command runs the first time + @Override + protected void initialize() { + } + + // Called repeatedly when this Command is scheduled to run + @Override + protected void execute() { + Robot.m_led.updateLED(); + } + + // Make this return true when this Command no longer needs to run execute() + @Override + protected boolean isFinished() { + return false; + } + + // Called once after isFinished returns true + @Override + protected void end() { + } + + // Called when another command which requires one or more of the same + // subsystems is scheduled to run + @Override + protected void interrupted() { + } +} diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 85cc1f1..8090480 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -67,12 +67,8 @@ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } - public void gamerMove(){ - m_driveTrain.arcadeDrive(0, 1); - } - @Override - public void initDefaultCommand() { + public void initDefaultCommand(){ // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); setDefaultCommand(new DriveWithJoystick()); diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 28fad8c..3c66878 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -8,6 +8,7 @@ package frc4388.robot.subsystems; import frc4388.robot.RobotMap; +import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.constants.LEDPatterns; import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.command.Subsystem; @@ -27,9 +28,8 @@ public LED(){ LEDController.set(currentLED); } - public void periodic() { + public void updateLED(){ LEDController.set(currentLED); - SmartDashboard.putNumber("LED", currentLED); } public void setPattern(LEDPatterns pattern){ @@ -37,7 +37,15 @@ public void setPattern(LEDPatterns pattern){ LEDController.set(currentLED); } + @Override + public void periodic(){ + SmartDashboard.putNumber("LED", currentLED); + } + @Override public void initDefaultCommand() { + // Set the default command for a subsystem here. + // setDefaultCommand(new MySpecialCommand()); + setDefaultCommand(new UpdateLED()); } } \ No newline at end of file From 84d47e5acdf0558f15225b7ded1a437a0e63da8b Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Dec 2019 15:34:53 -0700 Subject: [PATCH 11/70] Add meme text on robot init --- src/main/java/frc4388/robot/subsystems/LED.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 3c66878..3e94807 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -26,6 +26,7 @@ public LED(){ LEDController = new Spark(RobotMap.LED_SPARK_ID); setPattern(LEDPatterns.FOREST_WAVES); LEDController.set(currentLED); + System.err.println("In the Beginning, there was Joe.\nAnd he said, 'Let there be LEDs.'\nAnd it was good."); } public void updateLED(){ From b5ed4b12a037f3096315272cd9226ca161d22fd9 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 11:01:43 -0700 Subject: [PATCH 12/70] Import 2019 into 2020 --- .gitignore | 1 + .vscode/settings.json | 9 +- .wpilib/wpilib_preferences.json | 2 +- build.gradle | 29 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 55741 -> 58702 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 51 +++++++---- gradlew.bat | 18 +++- settings.gradle | 8 +- vendordeps/Phoenix.json | 111 +++++++++++++++++++++-- vendordeps/WPILibOldCommands.json | 37 ++++++++ 11 files changed, 218 insertions(+), 50 deletions(-) create mode 100644 vendordeps/WPILibOldCommands.json diff --git a/.gitignore b/.gitignore index 61f25ce..983678a 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,7 @@ gradle-app.setting .project .settings/ bin/ +imgui.ini # End of https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode diff --git a/.vscode/settings.json b/.vscode/settings.json index 860e319..5200b5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,8 +7,9 @@ "**/CVS": true, "**/.DS_Store": true, "bin/": true, - ".classpath": true, - ".project": true - }, - "wpilib.online": true + "**/.classpath": true, + "**/.project": true, + "**/.settings": true, + "**/.factorypath": true + } } diff --git a/.wpilib/wpilib_preferences.json b/.wpilib/wpilib_preferences.json index 95c6671..a5920a4 100644 --- a/.wpilib/wpilib_preferences.json +++ b/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": false, "currentLanguage": "java", - "projectYear": "2019", + "projectYear": "2020", "teamNumber": 4388 } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9216a46..6ae9dcf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,11 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2019.1.1" + id "edu.wpi.first.GradleRIO" version "2020.1.2" } +sourceCompatibility = JavaVersion.VERSION_11 +targetCompatibility = JavaVersion.VERSION_11 + def ROBOT_MAIN_CLASS = "frc4388.robot.Main" // Define my targets (RoboRIO) and artifacts (deployable files) @@ -35,27 +38,31 @@ deploy { } // Set this to true to enable desktop support. -def includeDesktopSupport = false - -// Maven central needed for JUnit -repositories { - mavenCentral() -} +def includeDesktopSupport = true // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 4. dependencies { - compile wpi.deps.wpilib() - compile wpi.deps.vendor.java() + implementation wpi.deps.wpilib() + nativeZip wpi.deps.wpilibJni(wpi.platforms.roborio) + nativeDesktopZip wpi.deps.wpilibJni(wpi.platforms.desktop) + + + implementation wpi.deps.vendor.java() nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) - testCompile 'junit:junit:4.12' + + testImplementation 'junit:junit:4.12' + + // Enable simulation gui support. Must check the box in vscode to enable support + // upon debugging + simulation wpi.deps.sim.gui(wpi.platforms.desktop, false) } // Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar') // in order to make them all available at runtime. Also adding the manifest so WPILib // knows where to look for our Robot Class. jar { - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 457aad0d98108420a977756b7145c93c8910b076..cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b 100644 GIT binary patch delta 25889 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

Vp_F!2 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 delta 23210 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 T8$G=9f_Op3$oWUH6=nB-yb}(9 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d08253c..d050f17 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/gradlew b/gradlew index af6708f..2fe81a7 100644 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -138,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 6d57edc..9618d8d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/settings.gradle b/settings.gradle index b0f4d48..81f96ab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,17 +4,19 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2019' + String frcYear = '2020' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') if (publicFolder == null) { publicFolder = "C:\\Users\\Public" } - frcHome = new File(publicFolder, "frc${frcYear}") + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) } else { def userFolder = System.getProperty("user.home") - frcHome = new File(userFolder, "frc${frcYear}") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) } def frcHomeMaven = new File(frcHome, 'maven') maven { diff --git a/vendordeps/Phoenix.json b/vendordeps/Phoenix.json index d4da1ce..f8d42a4 100644 --- a/vendordeps/Phoenix.json +++ b/vendordeps/Phoenix.json @@ -1,7 +1,7 @@ { "fileName": "Phoenix.json", "name": "CTRE-Phoenix", - "version": "5.12.0", + "version": "5.17.3", "uuid": "ab676553-b602-441f-a38d-f1296eff6537", "mavenUrls": [ "http://devsite.ctr-electronics.com/maven/release/" @@ -11,19 +11,65 @@ { "groupId": "com.ctre.phoenix", "artifactId": "api-java", - "version": "5.12.0" + "version": "5.17.3" }, { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-java", - "version": "5.12.0" + "version": "5.17.3" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.12.0", + "version": "5.17.3", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "diagnostics", + "version": "5.17.3", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "canutils", + "version": "5.17.3", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "platform-stub", + "version": "5.17.3", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "core", + "version": "5.17.3", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -37,7 +83,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-cpp", - "version": "5.12.0", + "version": "5.17.3", "libName": "CTRE_Phoenix_WPI", "headerClassifier": "headers", "sharedLibrary": false, @@ -51,7 +97,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "api-cpp", - "version": "5.12.0", + "version": "5.17.3", "libName": "CTRE_Phoenix", "headerClassifier": "headers", "sharedLibrary": false, @@ -65,7 +111,7 @@ { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.12.0", + "version": "5.17.3", "libName": "CTRE_PhoenixCCI", "headerClassifier": "headers", "sharedLibrary": false, @@ -76,12 +122,59 @@ "linuxx86-64" ] }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "diagnostics", + "version": "5.17.3", + "libName": "CTRE_PhoenixDiagnostics", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "canutils", + "version": "5.17.3", + "libName": "CTRE_PhoenixCanutils", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "platform-stub", + "version": "5.17.3", + "libName": "CTRE_PhoenixPlatform", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64" + ] + }, { "groupId": "com.ctre.phoenix", "artifactId": "core", - "version": "5.12.0", + "version": "5.17.3", "libName": "CTRE_PhoenixCore", - "headerClassifier": "headers" + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] } ] } \ No newline at end of file diff --git a/vendordeps/WPILibOldCommands.json b/vendordeps/WPILibOldCommands.json new file mode 100644 index 0000000..7bdad21 --- /dev/null +++ b/vendordeps/WPILibOldCommands.json @@ -0,0 +1,37 @@ +{ + "fileName": "WPILibOldCommands.json", + "name": "WPILib-Old-Commands", + "version": "2020.0.0", + "uuid": "b066afc2-5c18-43c4-b758-43381fcb275e", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibOldCommands", + "artifactId": "wpilibOldCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibOldCommands", + "artifactId": "wpilibOldCommands-cpp", + "version": "wpilib", + "libName": "wpilibOldCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxraspbian", + "linuxaarch64bionic", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxx86-64" + ] + } + ] +} From 8cf2d8c37184bdd7d473eef2e71566ceefc128d0 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 11:03:40 -0700 Subject: [PATCH 13/70] Removed unused lines of code --- src/main/java/frc4388/robot/Robot.java | 1 - src/main/java/frc4388/robot/RobotMap.java | 2 -- src/main/java/frc4388/robot/subsystems/Drive.java | 10 ---------- 3 files changed, 13 deletions(-) diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index aa44e07..6cd6668 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -16,7 +16,6 @@ import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.ExampleSubsystem; import frc4388.robot.subsystems.LED; -import frc4388.utility.controller.XboxController; /** * The VM is configured to automatically run this class, and to call the diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index fe7fe7e..67aff89 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -7,8 +7,6 @@ package frc4388.robot; -import frc4388.utility.controller.XboxController; - /** * The RobotMap is a mapping from the ports sensors and actuators are wired into * to a variable name. This provides flexibility changing wiring, makes checking diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 8090480..8d9b3b2 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -7,21 +7,13 @@ package frc4388.robot.subsystems; -import com.ctre.phoenix.motorcontrol.Faults; import com.ctre.phoenix.motorcontrol.InvertType; import com.ctre.phoenix.motorcontrol.NeutralMode; -import com.ctre.phoenix.motorcontrol.can.TalonSRX; import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; - -import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc4388.robot.RobotMap; import frc4388.robot.commands.Drive.DriveWithJoystick; -import frc4388.robot.commands.Drive.GamerMove; -import frc4388.robot.OI; -import frc4388.robot.Robot; -import frc4388.utility.controller.XboxController; /** * Add your docs here. @@ -36,8 +28,6 @@ public class Drive extends Subsystem { public static WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_BACK_CAN_ID); public static DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); - - private static double m_inputMove, m_inputSteer; public Drive(){ /* factory default values */ From b4434da999e6ce11c6d9ee2ba85d8a465b31a409 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 17:35:52 -0700 Subject: [PATCH 14/70] Implement new RobotContainer.java and Constants.java Classes - OI.java has been deprecated and replaced with RobotContainer.java - RobotMap.java has been deprecated and replaced with Constants.java - Changes have been made to use the new classes without errors; some of these changes will be changed in the future --- src/main/java/frc4388/robot/Constants.java | 34 +++++++++ src/main/java/frc4388/robot/OI.java | 2 + src/main/java/frc4388/robot/Robot.java | 22 ++---- .../java/frc4388/robot/RobotContainer.java | 70 +++++++++++++++++++ src/main/java/frc4388/robot/RobotMap.java | 2 + .../commands/Drive/DriveWithJoystick.java | 9 ++- .../robot/commands/Drive/GamerMove.java | 5 +- .../robot/commands/ExampleCommand.java | 48 ------------- .../robot/commands/LED/SetLEDPattern.java | 5 +- .../frc4388/robot/commands/LED/UpdateLED.java | 5 +- .../java/frc4388/robot/subsystems/Drive.java | 11 +-- .../robot/subsystems/ExampleSubsystem.java | 24 ------- .../java/frc4388/robot/subsystems/LED.java | 9 +-- 13 files changed, 134 insertions(+), 112 deletions(-) create mode 100644 src/main/java/frc4388/robot/Constants.java create mode 100644 src/main/java/frc4388/robot/RobotContainer.java delete mode 100644 src/main/java/frc4388/robot/commands/ExampleCommand.java delete mode 100644 src/main/java/frc4388/robot/subsystems/ExampleSubsystem.java diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java new file mode 100644 index 0000000..d5ab37e --- /dev/null +++ b/src/main/java/frc4388/robot/Constants.java @@ -0,0 +1,34 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot; + +/** + * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean + * constants. This class should not be used for any other purpose. All constants should be + * declared globally (i.e. public static). Do not put anything functional in this class. + * + *

It is advised to statically import this class (or one of its inner classes) wherever the + * constants are needed, to reduce verbosity. + */ +public final class Constants { + public static final class DriveConstants { + public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; + public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; + public static final int DRIVE_LEFT_BACK_CAN_ID = 3; + public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; + } + + public static final class LEDConstants { + public static final int LED_SPARK_ID = 0; + } + + public static final class OIConstants { + public static final int XBOX_DRIVER_ID = 0; + public static final int XBOX_OPERATOR_ID = 1; + } +} diff --git a/src/main/java/frc4388/robot/OI.java b/src/main/java/frc4388/robot/OI.java index e97b4bb..2232d03 100644 --- a/src/main/java/frc4388/robot/OI.java +++ b/src/main/java/frc4388/robot/OI.java @@ -17,6 +17,8 @@ /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. + * + * @deprecated */ public class OI { //// CREATING BUTTONS diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index 6cd6668..b2988cb 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -10,12 +10,6 @@ import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.command.Command; import edu.wpi.first.wpilibj.command.Scheduler; -import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import frc4388.robot.commands.ExampleCommand; -import frc4388.robot.subsystems.Drive; -import frc4388.robot.subsystems.ExampleSubsystem; -import frc4388.robot.subsystems.LED; /** * The VM is configured to automatically run this class, and to call the @@ -25,13 +19,8 @@ * project. */ public class Robot extends TimedRobot { - public static ExampleSubsystem m_subsystem = new ExampleSubsystem(); - public static LED m_led = new LED(); - public static Drive m_Drive = new Drive(); - public static OI m_oi; - Command m_autonomousCommand; - SendableChooser m_chooser = new SendableChooser<>(); + public static RobotContainer m_robotContainer; /** * This function is run when the robot is first started up and should be @@ -39,10 +28,9 @@ public class Robot extends TimedRobot { */ @Override public void robotInit() { - m_oi = new OI(); - m_chooser.setDefaultOption("Default Auto", new ExampleCommand()); - // chooser.addOption("My Auto", new MyAutoCommand()); - SmartDashboard.putData("Auto mode", m_chooser); + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); } /** @@ -84,7 +72,7 @@ public void disabledPeriodic() { */ @Override public void autonomousInit() { - m_autonomousCommand = m_chooser.getSelected(); + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); /* * String autoSelected = SmartDashboard.getString("Auto Selector", diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java new file mode 100644 index 0000000..7f4d50a --- /dev/null +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot; + +import edu.wpi.first.wpilibj.Joystick; +import edu.wpi.first.wpilibj.command.Command; +import edu.wpi.first.wpilibj.command.InstantCommand; +import frc4388.robot.Constants.*; +import frc4388.robot.subsystems.Drive; +import frc4388.robot.subsystems.LED; +import frc4388.utility.controller.IHandController; +import frc4388.utility.controller.XboxController; + +/** + * This class is where the bulk of the robot should be declared. Since + * Command-based is a "declarative" paradigm, very little robot logic should + * actually be handled in the {@link Robot} periodic methods (other than the + * scheduler calls). Instead, the structure of the robot (including subsystems, + * commands, and button mappings) should be declared here. + */ +public class RobotContainer { + /* Subsystems */ + public static final Drive m_robotDrive = new Drive(); + public static final LED m_robotLED = new LED(); + + /* Controllers */ + XboxController m_driverXbox = new XboxController(OIConstants.XBOX_DRIVER_ID); + XboxController m_operatorXbox = new XboxController(OIConstants.XBOX_OPERATOR_ID); + + /** + * The container for the robot. Contains subsystems, OI devices, and commands. + */ + public RobotContainer() { + + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + // no auto + return new InstantCommand(); + } + + public IHandController getDriverController() { + return m_driverXbox; + } + + public IHandController getOperatorController() + { + return m_operatorXbox; + } + + public Joystick getOperatorJoystick() + { + return m_operatorXbox.getJoyStick(); + } + + public Joystick getDriverJoystick() + { + return m_driverXbox.getJoyStick(); + } +} diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 67aff89..2fce0f5 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -12,6 +12,8 @@ * to a variable name. This provides flexibility changing wiring, makes checking * the wiring easier and significantly reduces the number of magic numbers * floating around. + * + * @deprecated */ public class RobotMap { // For example to map the left and right motors, you could define the diff --git a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java index 572b875..14bcf21 100644 --- a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java +++ b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java @@ -8,8 +8,8 @@ package frc4388.robot.commands.Drive; import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.OI; import frc4388.robot.Robot; +import frc4388.robot.RobotContainer; public class DriveWithJoystick extends Command { @@ -18,7 +18,6 @@ public class DriveWithJoystick extends Command { public DriveWithJoystick() { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); - requires(Robot.m_Drive); } // Called just before this Command runs the first time @@ -29,9 +28,9 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - m_inputMove = OI.getInstance().getDriverController().getLeftYAxis(); - m_inputSteer = -(OI.getInstance().getDriverController().getRightXAxis()); - Robot.m_Drive.driveWithInput(m_inputMove, m_inputSteer); + m_inputMove = Robot.m_robotContainer.getDriverController().getLeftYAxis(); + m_inputSteer = -(Robot.m_robotContainer.getDriverController().getRightXAxis()); + RobotContainer.m_robotDrive.driveWithInput(m_inputMove, m_inputSteer); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java index b890aa5..6169453 100644 --- a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java +++ b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java @@ -8,13 +8,12 @@ package frc4388.robot.commands.Drive; import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.Robot; +import frc4388.robot.RobotContainer; public class GamerMove extends Command { public GamerMove() { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); - requires(Robot.m_Drive); } // Called just before this Command runs the first time @@ -25,7 +24,7 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - Robot.m_Drive.driveWithInput(0, 1); + RobotContainer.m_robotDrive.driveWithInput(0, 1); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/commands/ExampleCommand.java b/src/main/java/frc4388/robot/commands/ExampleCommand.java deleted file mode 100644 index cb8a9f6..0000000 --- a/src/main/java/frc4388/robot/commands/ExampleCommand.java +++ /dev/null @@ -1,48 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.commands; - -import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.Robot; - -/** - * An example command. You can replace me with your own command. - */ -public class ExampleCommand extends Command { - public ExampleCommand() { - // Use requires() here to declare subsystem dependencies - requires(Robot.m_subsystem); - } - - // Called just before this Command runs the first time - @Override - protected void initialize() { - } - - // Called repeatedly when this Command is scheduled to run - @Override - protected void execute() { - } - - // Make this return true when this Command no longer needs to run execute() - @Override - protected boolean isFinished() { - return false; - } - - // Called once after isFinished returns true - @Override - protected void end() { - } - - // Called when another command which requires one or more of the same - // subsystems is scheduled to run - @Override - protected void interrupted() { - } -} diff --git a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java index 11c4447..152f81a 100644 --- a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java @@ -7,7 +7,7 @@ package frc4388.robot.commands.LED; -import frc4388.robot.Robot; +import frc4388.robot.RobotContainer; import frc4388.robot.constants.LEDPatterns; import edu.wpi.first.wpilibj.command.Command; @@ -17,7 +17,6 @@ public class SetLEDPattern extends Command { public static LEDPatterns m_pattern; public SetLEDPattern(LEDPatterns pattern) { - requires(Robot.m_led); m_pattern = pattern; } @@ -29,7 +28,7 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - Robot.m_led.setPattern(m_pattern); + RobotContainer.m_robotLED.setPattern(m_pattern); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java index 3d6c374..44f195c 100644 --- a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java +++ b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java @@ -8,12 +8,11 @@ package frc4388.robot.commands.LED; import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.Robot; +import frc4388.robot.RobotContainer; public class UpdateLED extends Command { public UpdateLED() { // Use requires() here to declare subsystem dependencies - requires(Robot.m_led); } // Called just before this Command runs the first time @@ -24,7 +23,7 @@ protected void initialize() { // Called repeatedly when this Command is scheduled to run @Override protected void execute() { - Robot.m_led.updateLED(); + RobotContainer.m_robotLED.updateLED(); } // Make this return true when this Command no longer needs to run execute() diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 8d9b3b2..135afd0 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -12,7 +12,8 @@ import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import frc4388.robot.RobotMap; + +import frc4388.robot.Constants.DriveConstants; import frc4388.robot.commands.Drive.DriveWithJoystick; /** @@ -22,10 +23,10 @@ public class Drive extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. - public static WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_FRONT_CAN_ID); - public static WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_FRONT_CAN_ID); - public static WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_LEFT_BACK_CAN_ID); - public static WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(RobotMap.DRIVE_RIGHT_BACK_CAN_ID); + public static WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); + public static WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); + public static WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); + public static WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); public static DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); diff --git a/src/main/java/frc4388/robot/subsystems/ExampleSubsystem.java b/src/main/java/frc4388/robot/subsystems/ExampleSubsystem.java deleted file mode 100644 index 50ace89..0000000 --- a/src/main/java/frc4388/robot/subsystems/ExampleSubsystem.java +++ /dev/null @@ -1,24 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.subsystems; - -import edu.wpi.first.wpilibj.command.Subsystem; - -/** - * An example subsystem. You can replace me with your own Subsystem. - */ -public class ExampleSubsystem extends Subsystem { - // Put methods for controlling this subsystem - // here. Call these from Commands. - - @Override - public void initDefaultCommand() { - // Set the default command for a subsystem here. - // setDefaultCommand(new MySpecialCommand()); - } -} diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 3c66878..7dc940c 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -7,13 +7,14 @@ package frc4388.robot.subsystems; -import frc4388.robot.RobotMap; -import frc4388.robot.commands.LED.UpdateLED; -import frc4388.robot.constants.LEDPatterns; import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc4388.robot.Constants.LEDConstants; +import frc4388.robot.commands.LED.UpdateLED; +import frc4388.robot.constants.LEDPatterns; + /** * Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED Driver */ @@ -23,7 +24,7 @@ public class LED extends Subsystem { public static Spark LEDController; public LED(){ - LEDController = new Spark(RobotMap.LED_SPARK_ID); + LEDController = new Spark(LEDConstants.LED_SPARK_ID); setPattern(LEDPatterns.FOREST_WAVES); LEDController.set(currentLED); } From 8317348f8dd8a6b5a29e553c4a5b00bf4002a779 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 18:59:50 -0700 Subject: [PATCH 15/70] Implement new Command Based Framework --- src/main/java/frc4388/robot/OI.java | 91 ------------------- src/main/java/frc4388/robot/Robot.java | 30 +++--- .../java/frc4388/robot/RobotContainer.java | 35 +++++-- src/main/java/frc4388/robot/RobotMap.java | 41 --------- .../commands/Drive/DriveWithJoystick.java | 52 +++++------ .../robot/commands/Drive/GamerMove.java | 45 ++++----- .../robot/commands/LED/SetLEDPattern.java | 46 +++------- .../frc4388/robot/commands/LED/UpdateLED.java | 44 ++++----- .../java/frc4388/robot/subsystems/Drive.java | 10 +- .../java/frc4388/robot/subsystems/LED.java | 12 +-- .../utility/controller/XboxTriggerButton.java | 3 +- vendordeps/WPILibNewCommands.json | 37 ++++++++ vendordeps/WPILibOldCommands.json | 37 -------- 13 files changed, 177 insertions(+), 306 deletions(-) delete mode 100644 src/main/java/frc4388/robot/OI.java delete mode 100644 src/main/java/frc4388/robot/RobotMap.java create mode 100644 vendordeps/WPILibNewCommands.json delete mode 100644 vendordeps/WPILibOldCommands.json diff --git a/src/main/java/frc4388/robot/OI.java b/src/main/java/frc4388/robot/OI.java deleted file mode 100644 index 2232d03..0000000 --- a/src/main/java/frc4388/robot/OI.java +++ /dev/null @@ -1,91 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot; - -import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.buttons.JoystickButton; -import frc4388.robot.commands.Drive.DriveWithJoystick; -import frc4388.robot.commands.Drive.GamerMove; -import frc4388.utility.controller.IHandController; -import frc4388.utility.controller.XboxController; - -/** - * This class is the glue that binds the controls on the physical operator - * interface to the commands and command groups that allow control of the robot. - * - * @deprecated - */ -public class OI { - //// CREATING BUTTONS - // One type of button is a joystick button which is any button on a - //// joystick. - // You create one by telling it which joystick it's on and which button - // number it is. - // Joystick stick = new Joystick(port); - // Button button = new JoystickButton(stick, buttonNumber); - - // There are a few additional built in buttons you can use. Additionally, - // by subclassing Button you can create custom triggers and bind those to - // commands the same as any other Button. - - //// TRIGGERING COMMANDS WITH BUTTONS - // Once you have a button, it's trivial to bind it to a button in one of - // three ways: - - // Start the command when the button is pressed and let it run the command - // until it is finished as determined by it's isFinished method. - // button.whenPressed(new ExampleCommand()); - - // Run the command while the button is being held down and interrupt it once - // the button is released. - // button.whileHeld(new ExampleCommand()); - - // Start the command when the button is released and let it run the command - // until it is finished as determined by it's isFinished method. - // button.whenReleased(new ExampleCommand()); - - private static OI instance; - - public static XboxController m_driverXbox; - private static XboxController m_operatorXbox; - - public OI() { - m_driverXbox = new XboxController(RobotMap.XBOX_DRIVER_ID); - m_operatorXbox = new XboxController(RobotMap.XBOX_OPERATOR_ID); - - JoystickButton GamerMove = new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON); - GamerMove.whenPressed(new GamerMove()); - GamerMove.whenReleased(new DriveWithJoystick()); - } - - public static OI getInstance() { - if(instance == null) { - instance = new OI(); - } - return instance; - } - - public IHandController getDriverController() { - return m_driverXbox; - } - - public IHandController getOperatorController() - { - return m_operatorXbox; - } - - public Joystick getOperatorJoystick() - { - return m_operatorXbox.getJoyStick(); - } - - public Joystick getDriverJoystick() - { - return m_driverXbox.getJoyStick(); - } -} diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index b2988cb..7735364 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -8,8 +8,8 @@ package frc4388.robot; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.command.Command; -import edu.wpi.first.wpilibj.command.Scheduler; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; /** * The VM is configured to automatically run this class, and to call the @@ -20,7 +20,8 @@ */ public class Robot extends TimedRobot { Command m_autonomousCommand; - public static RobotContainer m_robotContainer; + + private RobotContainer m_robotContainer; /** * This function is run when the robot is first started up and should be @@ -43,6 +44,11 @@ public void robotInit() { */ @Override public void robotPeriodic() { + // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled + // commands, running already-scheduled commands, removing finished or interrupted commands, + // and running subsystem periodic() methods. This must be called from the robot's periodic + // block in order for anything in the Command-based framework to work. + CommandScheduler.getInstance().run(); } /** @@ -56,19 +62,10 @@ public void disabledInit() { @Override public void disabledPeriodic() { - Scheduler.getInstance().run(); } /** - * This autonomous (along with the chooser code above) shows how to select - * between different autonomous modes using the dashboard. The sendable - * chooser code works with the Java SmartDashboard. If you prefer the - * LabVIEW Dashboard, remove all of the chooser code and uncomment the - * getString code to get the auto name from the text box below the Gyro - * - *

You can add additional auto modes by adding additional commands to the - * chooser code above (like the commented example) or additional comparisons - * to the switch structure below with additional strings & commands. + * This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ @Override public void autonomousInit() { @@ -83,7 +80,7 @@ public void autonomousInit() { // schedule the autonomous command (example) if (m_autonomousCommand != null) { - m_autonomousCommand.start(); + m_autonomousCommand.schedule(); } } @@ -92,7 +89,6 @@ public void autonomousInit() { */ @Override public void autonomousPeriodic() { - Scheduler.getInstance().run(); } @Override @@ -111,7 +107,7 @@ public void teleopInit() { */ @Override public void teleopPeriodic() { - Scheduler.getInstance().run(); + } /** diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 7f4d50a..9ba1572 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -8,9 +8,13 @@ package frc4388.robot; import edu.wpi.first.wpilibj.Joystick; -import edu.wpi.first.wpilibj.command.Command; -import edu.wpi.first.wpilibj.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; +import frc4388.robot.commands.Drive.DriveWithJoystick; +import frc4388.robot.commands.Drive.GamerMove; +import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.LED; import frc4388.utility.controller.IHandController; @@ -25,18 +29,34 @@ */ public class RobotContainer { /* Subsystems */ - public static final Drive m_robotDrive = new Drive(); - public static final LED m_robotLED = new LED(); + private final Drive m_robotDrive = new Drive(); + private final LED m_robotLED = new LED(); /* Controllers */ - XboxController m_driverXbox = new XboxController(OIConstants.XBOX_DRIVER_ID); - XboxController m_operatorXbox = new XboxController(OIConstants.XBOX_OPERATOR_ID); + private final XboxController m_driverXbox = new XboxController(OIConstants.XBOX_DRIVER_ID); + private final XboxController m_operatorXbox = new XboxController(OIConstants.XBOX_OPERATOR_ID); /** * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { - + configureButtonBindings(); + + /* Default Commands */ + m_robotDrive.setDefaultCommand(new DriveWithJoystick(m_robotDrive, getDriverController())); + m_robotLED.setDefaultCommand(new UpdateLED(m_robotLED)); + } + + /** + * Use this method to define your button->command mappings. Buttons can be created by + * instantiating a {@link GenericHID} or one of its subclasses ({@link + * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a + * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. + */ + private void configureButtonBindings() { + new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) + .whenPressed(new GamerMove(m_robotDrive)) + .whenReleased(new DriveWithJoystick(m_robotDrive, getDriverController())); } /** @@ -49,6 +69,7 @@ public Command getAutonomousCommand() { return new InstantCommand(); } + public IHandController getDriverController() { return m_driverXbox; } diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java deleted file mode 100644 index 2fce0f5..0000000 --- a/src/main/java/frc4388/robot/RobotMap.java +++ /dev/null @@ -1,41 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot; - -/** - * The RobotMap is a mapping from the ports sensors and actuators are wired into - * to a variable name. This provides flexibility changing wiring, makes checking - * the wiring easier and significantly reduces the number of magic numbers - * floating around. - * - * @deprecated - */ -public class RobotMap { - // For example to map the left and right motors, you could define the - // following variables to use with your drivetrain subsystem. - // public static int leftMotor = 1; - // public static int rightMotor = 2; - - // If you are using multiple modules, make sure to define both the port - // number and the module. For example you with a rangefinder: - // public static int rangefinderPort = 1; - // public static int rangefinderModule = 1; - - /* Xbox Controllers */ - public static final int XBOX_DRIVER_ID = 0; - public static final int XBOX_OPERATOR_ID = 1; - - /* Blinkin LED Strip */ - public static final int LED_SPARK_ID = 0; - - /* Drive Train */ - public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; - public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; - public static final int DRIVE_LEFT_BACK_CAN_ID = 3; - public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; -} diff --git a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java index 14bcf21..04b6870 100644 --- a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java +++ b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,46 +7,46 @@ package frc4388.robot.commands.Drive; -import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.Robot; -import frc4388.robot.RobotContainer; +import edu.wpi.first.wpilibj2.command.CommandBase; +import frc4388.robot.subsystems.Drive; +import frc4388.utility.controller.IHandController; -public class DriveWithJoystick extends Command { +public class DriveWithJoystick extends CommandBase { + private final Drive m_drive; + private final IHandController m_driverXbox; public double m_inputMove, m_inputSteer; - public DriveWithJoystick() { - // Use requires() here to declare subsystem dependencies - // eg. requires(chassis); + /** + * Creates a new DriveWithJoystick, driving the robot with the given controller + */ + public DriveWithJoystick(Drive subsystem, IHandController controller) { + m_drive = subsystem; + m_driverXbox = controller; + addRequirements(m_drive); } - // Called just before this Command runs the first time + // Called when the command is initially scheduled. @Override - protected void initialize() { + public void initialize() { } - // Called repeatedly when this Command is scheduled to run + // Called every time the scheduler runs while the command is scheduled. @Override - protected void execute() { - m_inputMove = Robot.m_robotContainer.getDriverController().getLeftYAxis(); - m_inputSteer = -(Robot.m_robotContainer.getDriverController().getRightXAxis()); - RobotContainer.m_robotDrive.driveWithInput(m_inputMove, m_inputSteer); + public void execute() { + m_inputMove = m_driverXbox.getLeftYAxis(); + m_inputSteer = m_driverXbox.getRightXAxis(); + m_drive.driveWithInput(m_inputMove, m_inputSteer); } - // Make this return true when this Command no longer needs to run execute() + // Called once the command ends or is interrupted. @Override - protected boolean isFinished() { - return false; + public void end(boolean interrupted) { } - // Called once after isFinished returns true + // Returns true when the command should end. @Override - protected void end() { - } - - // Called when another command which requires one or more of the same - // subsystems is scheduled to run - @Override - protected void interrupted() { + public boolean isFinished() { + return false; } } diff --git a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java index 6169453..d42fe4a 100644 --- a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java +++ b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,40 +7,41 @@ package frc4388.robot.commands.Drive; -import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.RobotContainer; +import edu.wpi.first.wpilibj2.command.CommandBase; -public class GamerMove extends Command { - public GamerMove() { - // Use requires() here to declare subsystem dependencies - // eg. requires(chassis); - } +import frc4388.robot.subsystems.Drive; - // Called just before this Command runs the first time - @Override - protected void initialize() { +public class GamerMove extends CommandBase { + + private final Drive m_drive; + + /** + * Creates a new GamerMove. + */ + public GamerMove(Drive subsystem) { + m_drive = subsystem; + addRequirements(m_drive); } - // Called repeatedly when this Command is scheduled to run + // Called when the command is initially scheduled. @Override - protected void execute() { - RobotContainer.m_robotDrive.driveWithInput(0, 1); + public void initialize() { } - // Make this return true when this Command no longer needs to run execute() + // Called every time the scheduler runs while the command is scheduled. @Override - protected boolean isFinished() { - return false; + public void execute() { + m_drive.driveWithInput(0, 1); } - // Called once after isFinished returns true + // Called once the command ends or is interrupted. @Override - protected void end() { + public void end(boolean interrupted) { } - // Called when another command which requires one or more of the same - // subsystems is scheduled to run + // Returns true when the command should end. @Override - protected void interrupted() { + public boolean isFinished() { + return false; } } diff --git a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java index 152f81a..785d23b 100644 --- a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,44 +7,28 @@ package frc4388.robot.commands.LED; -import frc4388.robot.RobotContainer; -import frc4388.robot.constants.LEDPatterns; +import edu.wpi.first.wpilibj2.command.InstantCommand; -import edu.wpi.first.wpilibj.command.Command; +import frc4388.robot.constants.LEDPatterns; +import frc4388.robot.subsystems.LED; -public class SetLEDPattern extends Command { +// NOTE: Consider using this command inline, rather than writing a subclass. For more +// information, see: +// https://docs.wpilib.org/en/latest/docs/software/commandbased/convenience-features.html +public class SetLEDPattern extends InstantCommand { + private final LED m_led; public static LEDPatterns m_pattern; - public SetLEDPattern(LEDPatterns pattern) { + public SetLEDPattern(LED subsystem, LEDPatterns pattern) { + m_led = subsystem; m_pattern = pattern; + addRequirements(m_led); } - // Called just before this Command runs the first time - @Override - protected void initialize() { - } - - // Called repeatedly when this Command is scheduled to run - @Override - protected void execute() { - RobotContainer.m_robotLED.setPattern(m_pattern); - } - - // Make this return true when this Command no longer needs to run execute() - @Override - protected boolean isFinished() { - return true; - } - - // Called once after isFinished returns true - @Override - protected void end() { - } - - // Called when another command which requires one or more of the same - // subsystems is scheduled to run + // Called when the command is initially scheduled. @Override - protected void interrupted() { + public void initialize() { + m_led.setPattern(m_pattern); } } diff --git a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java index 44f195c..7baab4e 100644 --- a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java +++ b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,39 +7,41 @@ package frc4388.robot.commands.LED; -import edu.wpi.first.wpilibj.command.Command; -import frc4388.robot.RobotContainer; +import edu.wpi.first.wpilibj2.command.CommandBase; -public class UpdateLED extends Command { - public UpdateLED() { - // Use requires() here to declare subsystem dependencies - } +import frc4388.robot.subsystems.LED; - // Called just before this Command runs the first time - @Override - protected void initialize() { +public class UpdateLED extends CommandBase { + + private final LED m_LED; + + /** + * Creates a new UpdateLED that continually runs updateLED in the LED subsystem. + */ + public UpdateLED(LED subsystem) { + m_LED = subsystem; + addRequirements(m_LED); } - // Called repeatedly when this Command is scheduled to run + // Called when the command is initially scheduled. @Override - protected void execute() { - RobotContainer.m_robotLED.updateLED(); + public void initialize() { } - // Make this return true when this Command no longer needs to run execute() + // Called every time the scheduler runs while the command is scheduled. @Override - protected boolean isFinished() { - return false; + public void execute() { + m_LED.updateLED(); } - // Called once after isFinished returns true + // Called once the command ends or is interrupted. @Override - protected void end() { + public void end(boolean interrupted) { } - // Called when another command which requires one or more of the same - // subsystems is scheduled to run + // Returns true when the command should end. @Override - protected void interrupted() { + public boolean isFinished() { + return false; } } diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 135afd0..b5edc2f 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -10,16 +10,16 @@ import com.ctre.phoenix.motorcontrol.InvertType; import com.ctre.phoenix.motorcontrol.NeutralMode; import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; -import edu.wpi.first.wpilibj.command.Subsystem; + import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.DriveConstants; -import frc4388.robot.commands.Drive.DriveWithJoystick; /** * Add your docs here. */ -public class Drive extends Subsystem { +public class Drive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. @@ -58,10 +58,10 @@ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } - @Override + /* @Override public void initDefaultCommand(){ // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); setDefaultCommand(new DriveWithJoystick()); - } + } */ } diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 7dc940c..f176ea3 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -8,17 +8,17 @@ package frc4388.robot.subsystems; import edu.wpi.first.wpilibj.Spark; -import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.LEDConstants; -import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.constants.LEDPatterns; /** - * Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED Driver + * Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED + * Driver */ - public class LED extends Subsystem { +public class LED extends SubsystemBase { public static float currentLED; public static Spark LEDController; @@ -43,10 +43,10 @@ public void periodic(){ SmartDashboard.putNumber("LED", currentLED); } - @Override + /* @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); setDefaultCommand(new UpdateLED()); - } + } */ } \ No newline at end of file diff --git a/src/main/java/frc4388/utility/controller/XboxTriggerButton.java b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java index aed4436..26372c2 100644 --- a/src/main/java/frc4388/utility/controller/XboxTriggerButton.java +++ b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java @@ -1,9 +1,8 @@ package frc4388.utility.controller; +import edu.wpi.first.wpilibj2.command.button.Button; import frc4388.utility.controller.XboxController; -import edu.wpi.first.wpilibj.buttons.Button; - /** * Mapping for the Xbox controller triggers to allow triggers to be defined as * buttons in {@link frc4388.robot.OI}. Checks to see if the given trigger diff --git a/vendordeps/WPILibNewCommands.json b/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..83de291 --- /dev/null +++ b/vendordeps/WPILibNewCommands.json @@ -0,0 +1,37 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "2020.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-cpp", + "version": "wpilib", + "libName": "wpilibNewCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxraspbian", + "linuxaarch64bionic", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxx86-64" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/WPILibOldCommands.json b/vendordeps/WPILibOldCommands.json deleted file mode 100644 index 7bdad21..0000000 --- a/vendordeps/WPILibOldCommands.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "fileName": "WPILibOldCommands.json", - "name": "WPILib-Old-Commands", - "version": "2020.0.0", - "uuid": "b066afc2-5c18-43c4-b758-43381fcb275e", - "mavenUrls": [], - "jsonUrl": "", - "javaDependencies": [ - { - "groupId": "edu.wpi.first.wpilibOldCommands", - "artifactId": "wpilibOldCommands-java", - "version": "wpilib" - } - ], - "jniDependencies": [], - "cppDependencies": [ - { - "groupId": "edu.wpi.first.wpilibOldCommands", - "artifactId": "wpilibOldCommands-cpp", - "version": "wpilib", - "libName": "wpilibOldCommands", - "headerClassifier": "headers", - "sourcesClassifier": "sources", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "linuxathena", - "linuxraspbian", - "linuxaarch64bionic", - "windowsx86-64", - "windowsx86", - "linuxx86-64", - "osxx86-64" - ] - } - ] -} From 5d38f7c24e409ea384047fc954bb3b9927c9d878 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 20:23:52 -0700 Subject: [PATCH 16/70] Move LEDPatterns.java to utility --- .../robot/commands/LED/SetLEDPattern.java | 2 +- src/main/java/frc4388/robot/subsystems/LED.java | 2 +- .../constants => utility}/LEDPatterns.java | 17 ++++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) rename src/main/java/frc4388/{robot/constants => utility}/LEDPatterns.java (91%) diff --git a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java index 785d23b..088a75d 100644 --- a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java @@ -9,8 +9,8 @@ import edu.wpi.first.wpilibj2.command.InstantCommand; -import frc4388.robot.constants.LEDPatterns; import frc4388.robot.subsystems.LED; +import frc4388.utility.LEDPatterns; // NOTE: Consider using this command inline, rather than writing a subclass. For more // information, see: diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index c7ed045..02c61ba 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -12,7 +12,7 @@ import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.LEDConstants; -import frc4388.robot.constants.LEDPatterns; +import frc4388.utility.LEDPatterns; /** * Allows for the control of a 5v LED Strip using a Rev Robotics Blinkin LED diff --git a/src/main/java/frc4388/robot/constants/LEDPatterns.java b/src/main/java/frc4388/utility/LEDPatterns.java similarity index 91% rename from src/main/java/frc4388/robot/constants/LEDPatterns.java rename to src/main/java/frc4388/utility/LEDPatterns.java index dc18958..6df032c 100644 --- a/src/main/java/frc4388/robot/constants/LEDPatterns.java +++ b/src/main/java/frc4388/utility/LEDPatterns.java @@ -1,7 +1,10 @@ -package frc4388.robot.constants; +package frc4388.utility; +/** + * Add your docs here. + */ public enum LEDPatterns { - // PALLETTE PATTERNS + /* PALLETTE PATTERNS */ RAINBOW_RAINBOW(-0.99f), PARTY_RAINBOW(-0.97f), OCEAN_RAINBOW(-0.95f), LAVA_RAINBOW(-0.93f), FOREST_RAINBOW(-0.91f), RAINBOW_GLITTER(-0.89f), CONFETTI(-0.87f), RED_SHOT(-0.85f), BLUE_SHOT(-0.83f), WHITE_SHOT(-0.81f), RAINBOW_SINELON(-0.79f), PARTY_SINELON(-0.77f), OCEAN_SINELON(-0.75f), LAVA_SINELON(-0.73f), FOREST_SINELON(-0.71f), RAINBOW_BPM(-0.69f), @@ -12,26 +15,26 @@ public enum LEDPatterns { BLUE_HEARTBEAT(-0.23f), WHITE_HEARTBEAT(-0.21f), GRAY_HEARBEAT(-0.19f), RED_BREATH(-0.17f), BLUE_BREATH(-0.15f), GRAY_BREATH(-0.13f), RED_STROBE(-0.11f), BLUE_STROBE(-0.09f), GOLD_STROBE(-0.07f), WHITE_STROBE(-0.05f), - // COLOR 1 PATTERNS + /* COLOR 1 PATTERNS */ C1_END_TO_END(-0.03f), C1_SCANNER(-0.01f), C1_CHASE(0.01f), C1_HEARTBEAT_SLOW(0.03f), C1_HEARTBEAT_MEDIUM(0.05f), C1_HEARTBEAT_FAST(0.07f), C1_BREATH_SLOW(0.09f), C1_BREATH_FAST(0.11f), C1_SHOT(0.13f), C1_STROBE(0.15f), - // COLOR 2 PATTERNS + /* COLOR 2 PATTERNS */ C2_END_TO_END(0.17f), C2_SCANNER(0.19f), C2_CHASE(0.21f), C2_HEARTBEAT_SLOW(0.23f), C2_HEARTBEAT_MEDIUM(0.25f), C2_HEARTBEAT_FAST(0.27f), C2_BREATH_SLOW(0.29f), C2_BREATH_FAST(0.31f), C2_SHOT(0.33f), C2_STROBE(0.35f), - // COLOR 1 AND 2 PATTERNS + /* COLOR 1 AND 2 PATTERNS */ C1C2_SPARKLE(0.37f), C2C1_SPARKLE(0.39f), C1C2_GRADIENT(0.41f), C1C2_BPM(0.43f), C1C2_BLEND(0.45f), C1C2_TWINKLES(0.51f), C1C2_WAVES(0.53f), C1C2_SINELON(0.55f), - // SOLID COLORS + /* SOLID COLORS */ SOLID_PINK_HOT(0.57f), SOLID_RED_DARK(0.59f), SOLID_RED(0.61f), SOLID_RED_ORANGE(0.63f), SOLID_ORANGE(0.65f), SOLID_GOLD(0.67f), SOLID_YELLOW(0.69f), SOLID_GREEN_LAWN(0.71f), SOLID_GREEN_LIME(0.73f), SOLID_GREEN_DARK(0.75f), SOLID_GREEN(0.77f), SOLID_BLUE_GREEN(0.79f), SOLID_BLUE_AQUA(0.81f), SOLID_BLUE_SKY(0.83f), SOLID_BLUE_DARK(0.85f), SOLID_BLUE(0.87f), SOLID_BLUE_VIOLET(0.89f), SOLID_VIOLET(0.91f), SOLID_WHITE(0.93f), SOLID_GRAY(0.95f), SOLID_GRAY_DARK(0.97f), SOLID_BLACK(0.99f); - // GETTERS/SETTERS + /* GETTERS/SETTERS */ private final float id; LEDPatterns(float id) { this.id = id; From 2f8ad5f7f9ff47e90247e2d7a4b58a55e81a3236 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 20:27:01 -0700 Subject: [PATCH 17/70] Added "Add your docs here" flags to all needed methods - All methods that need javadoc have been marked with /** * Add your docs here. */ --- src/main/java/frc4388/robot/RobotContainer.java | 13 ++++++++++++- .../frc4388/robot/commands/LED/SetLEDPattern.java | 3 +++ src/main/java/frc4388/robot/subsystems/Drive.java | 6 ++++++ src/main/java/frc4388/robot/subsystems/LED.java | 9 +++++++++ .../frc4388/utility/controller/IHandController.java | 3 +++ .../frc4388/utility/controller/XboxController.java | 6 ++++++ 6 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 9ba1572..e3702f4 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -69,21 +69,32 @@ public Command getAutonomousCommand() { return new InstantCommand(); } - + /** + * Add your docs here. + */ public IHandController getDriverController() { return m_driverXbox; } + /** + * Add your docs here. + */ public IHandController getOperatorController() { return m_operatorXbox; } + /** + * Add your docs here. + */ public Joystick getOperatorJoystick() { return m_operatorXbox.getJoyStick(); } + /** + * Add your docs here. + */ public Joystick getDriverJoystick() { return m_driverXbox.getJoyStick(); diff --git a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java index 088a75d..a7e793e 100644 --- a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java +++ b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java @@ -20,6 +20,9 @@ public class SetLEDPattern extends InstantCommand { private final LED m_led; public static LEDPatterns m_pattern; + /** + * Add your docs here. + */ public SetLEDPattern(LED subsystem, LEDPatterns pattern) { m_led = subsystem; m_pattern = pattern; diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index b5edc2f..d39260f 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -30,6 +30,9 @@ public class Drive extends SubsystemBase { public static DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + /** + * Add your docs here. + */ public Drive(){ /* factory default values */ m_leftFrontMotor.configFactoryDefault(); @@ -54,6 +57,9 @@ public Drive(){ m_rightBackMotor.setInverted(InvertType.FollowMaster); } + /** + * Add your docs here. + */ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 02c61ba..daf5751 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -23,6 +23,9 @@ public class LED extends SubsystemBase { public static float currentLED; public static Spark LEDController; + /** + * Add your docs here. + */ public LED(){ LEDController = new Spark(LEDConstants.LED_SPARK_ID); setPattern(LEDPatterns.FOREST_WAVES); @@ -30,10 +33,16 @@ public LED(){ System.err.println("In the Beginning, there was Joe.\nAnd he said, 'Let there be LEDs.'\nAnd it was good."); } + /** + * Add your docs here. + */ public void updateLED(){ LEDController.set(currentLED); } + /** + * Add your docs here. + */ public void setPattern(LEDPatterns pattern){ currentLED = pattern.getValue(); LEDController.set(currentLED); diff --git a/src/main/java/frc4388/utility/controller/IHandController.java b/src/main/java/frc4388/utility/controller/IHandController.java index 40ba864..13aa763 100644 --- a/src/main/java/frc4388/utility/controller/IHandController.java +++ b/src/main/java/frc4388/utility/controller/IHandController.java @@ -1,5 +1,8 @@ package frc4388.utility.controller; +/** + * Add your docs here. + */ public interface IHandController { public double getLeftXAxis(); diff --git a/src/main/java/frc4388/utility/controller/XboxController.java b/src/main/java/frc4388/utility/controller/XboxController.java index 83dcaf4..8b8f0f8 100644 --- a/src/main/java/frc4388/utility/controller/XboxController.java +++ b/src/main/java/frc4388/utility/controller/XboxController.java @@ -52,10 +52,16 @@ public class XboxController implements IHandController private Joystick m_stick; + /** + * Add your docs here. + */ public XboxController(int portNumber){ m_stick = new Joystick(portNumber); } + /** + * Add your docs here. + */ public Joystick getJoyStick() { return m_stick; } From 4879937c11f7f2a4e8ee798b95aa37d5f08a66ff Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 20:27:47 -0700 Subject: [PATCH 18/70] Remove Unusable Code --- src/main/java/frc4388/robot/subsystems/Drive.java | 7 ------- src/main/java/frc4388/robot/subsystems/LED.java | 7 ------- 2 files changed, 14 deletions(-) diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index d39260f..af19272 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -63,11 +63,4 @@ public Drive(){ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } - - /* @Override - public void initDefaultCommand(){ - // Set the default command for a subsystem here. - // setDefaultCommand(new MySpecialCommand()); - setDefaultCommand(new DriveWithJoystick()); - } */ } diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index daf5751..1b9008c 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -52,11 +52,4 @@ public void setPattern(LEDPatterns pattern){ public void periodic(){ SmartDashboard.putNumber("LED", currentLED); } - - /* @Override - public void initDefaultCommand() { - // Set the default command for a subsystem here. - // setDefaultCommand(new MySpecialCommand()); - setDefaultCommand(new UpdateLED()); - } */ } \ No newline at end of file From 52e1ec35d4fa256c3859912323c875d64cb1138f Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 20:47:09 -0700 Subject: [PATCH 19/70] Change GamerMove into an inline command --- .../java/frc4388/robot/RobotContainer.java | 3 +- .../robot/commands/Drive/GamerMove.java | 47 ------------------- 2 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 src/main/java/frc4388/robot/commands/Drive/GamerMove.java diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 9ba1572..4f86d17 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -13,7 +13,6 @@ import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; import frc4388.robot.commands.Drive.DriveWithJoystick; -import frc4388.robot.commands.Drive.GamerMove; import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.LED; @@ -55,7 +54,7 @@ public RobotContainer() { */ private void configureButtonBindings() { new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) - .whenPressed(new GamerMove(m_robotDrive)) + .whenPressed(() -> m_robotDrive.driveWithInput(0, 1)) .whenReleased(new DriveWithJoystick(m_robotDrive, getDriverController())); } diff --git a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java b/src/main/java/frc4388/robot/commands/Drive/GamerMove.java deleted file mode 100644 index d42fe4a..0000000 --- a/src/main/java/frc4388/robot/commands/Drive/GamerMove.java +++ /dev/null @@ -1,47 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.commands.Drive; - -import edu.wpi.first.wpilibj2.command.CommandBase; - -import frc4388.robot.subsystems.Drive; - -public class GamerMove extends CommandBase { - - private final Drive m_drive; - - /** - * Creates a new GamerMove. - */ - public GamerMove(Drive subsystem) { - m_drive = subsystem; - addRequirements(m_drive); - } - - // Called when the command is initially scheduled. - @Override - public void initialize() { - } - - // Called every time the scheduler runs while the command is scheduled. - @Override - public void execute() { - m_drive.driveWithInput(0, 1); - } - - // Called once the command ends or is interrupted. - @Override - public void end(boolean interrupted) { - } - - // Returns true when the command should end. - @Override - public boolean isFinished() { - return false; - } -} From 22df96518b1c69f8791c613bac6c7a247917fdc7 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 21:06:38 -0700 Subject: [PATCH 20/70] Add Documentation to Commands in RobotContainer.java --- src/main/java/frc4388/robot/RobotContainer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 4406d88..fe2e6c2 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -43,6 +43,8 @@ public RobotContainer() { /* Default Commands */ m_robotDrive.setDefaultCommand(new DriveWithJoystick(m_robotDrive, getDriverController())); + // drives the robot with a two-axis input from the driver controller + // continually sends updates to the Blinkin LED controller to keep the lights on m_robotLED.setDefaultCommand(new UpdateLED(m_robotLED)); } @@ -53,9 +55,13 @@ public RobotContainer() { * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. */ private void configureButtonBindings() { + /* Driver Buttons */ + //Test command to spin the robot while pressing A on the driver controller new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) .whenPressed(() -> m_robotDrive.driveWithInput(0, 1)) .whenReleased(new DriveWithJoystick(m_robotDrive, getDriverController())); + + /* Operator Buttons */ } /** From 024cf1138900d1670865c50b8f70e1d24e8750a8 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 21:07:16 -0700 Subject: [PATCH 21/70] Replaced DriveWithJoystick with Inline Command --- .../java/frc4388/robot/RobotContainer.java | 9 ++-- .../commands/Drive/DriveWithJoystick.java | 52 ------------------- 2 files changed, 5 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index fe2e6c2..ee3506c 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -10,9 +10,9 @@ import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; -import frc4388.robot.commands.Drive.DriveWithJoystick; import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.LED; @@ -42,8 +42,10 @@ public RobotContainer() { configureButtonBindings(); /* Default Commands */ - m_robotDrive.setDefaultCommand(new DriveWithJoystick(m_robotDrive, getDriverController())); // drives the robot with a two-axis input from the driver controller + m_robotDrive.setDefaultCommand(new RunCommand(() -> m_robotDrive.driveWithInput( + getDriverController().getLeftYAxis(), + getDriverController().getRightXAxis()))); // continually sends updates to the Blinkin LED controller to keep the lights on m_robotLED.setDefaultCommand(new UpdateLED(m_robotLED)); } @@ -58,8 +60,7 @@ private void configureButtonBindings() { /* Driver Buttons */ //Test command to spin the robot while pressing A on the driver controller new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) - .whenPressed(() -> m_robotDrive.driveWithInput(0, 1)) - .whenReleased(new DriveWithJoystick(m_robotDrive, getDriverController())); + .whileHeld(() -> m_robotDrive.driveWithInput(0, 1)); /* Operator Buttons */ } diff --git a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java b/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java deleted file mode 100644 index 04b6870..0000000 --- a/src/main/java/frc4388/robot/commands/Drive/DriveWithJoystick.java +++ /dev/null @@ -1,52 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.commands.Drive; - -import edu.wpi.first.wpilibj2.command.CommandBase; -import frc4388.robot.subsystems.Drive; -import frc4388.utility.controller.IHandController; - -public class DriveWithJoystick extends CommandBase { - - private final Drive m_drive; - private final IHandController m_driverXbox; - public double m_inputMove, m_inputSteer; - - /** - * Creates a new DriveWithJoystick, driving the robot with the given controller - */ - public DriveWithJoystick(Drive subsystem, IHandController controller) { - m_drive = subsystem; - m_driverXbox = controller; - addRequirements(m_drive); - } - - // Called when the command is initially scheduled. - @Override - public void initialize() { - } - - // Called every time the scheduler runs while the command is scheduled. - @Override - public void execute() { - m_inputMove = m_driverXbox.getLeftYAxis(); - m_inputSteer = m_driverXbox.getRightXAxis(); - m_drive.driveWithInput(m_inputMove, m_inputSteer); - } - - // Called once the command ends or is interrupted. - @Override - public void end(boolean interrupted) { - } - - // Returns true when the command should end. - @Override - public boolean isFinished() { - return false; - } -} From 1f83c35406831272a7edb92d660b32e5edeb833a Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 5 Jan 2020 21:26:49 -0700 Subject: [PATCH 22/70] Replaced LED Commands with Inline Commands --- src/main/java/frc4388/robot/Constants.java | 4 ++ .../java/frc4388/robot/RobotContainer.java | 10 ++-- .../robot/commands/LED/SetLEDPattern.java | 37 --------------- .../frc4388/robot/commands/LED/UpdateLED.java | 47 ------------------- .../java/frc4388/robot/subsystems/LED.java | 2 +- 5 files changed, 12 insertions(+), 88 deletions(-) delete mode 100644 src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java delete mode 100644 src/main/java/frc4388/robot/commands/LED/UpdateLED.java diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index d5ab37e..933908d 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -7,6 +7,8 @@ package frc4388.robot; +import frc4388.utility.LEDPatterns; + /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean * constants. This class should not be used for any other purpose. All constants should be @@ -25,6 +27,8 @@ public static final class DriveConstants { public static final class LEDConstants { public static final int LED_SPARK_ID = 0; + + public static final LEDPatterns DEFAULT_PATTERN = LEDPatterns.FOREST_WAVES; } public static final class OIConstants { diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index ee3506c..5421acf 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -13,9 +13,9 @@ import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; -import frc4388.robot.commands.LED.UpdateLED; import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.LED; +import frc4388.utility.LEDPatterns; import frc4388.utility.controller.IHandController; import frc4388.utility.controller.XboxController; @@ -47,7 +47,7 @@ public RobotContainer() { getDriverController().getLeftYAxis(), getDriverController().getRightXAxis()))); // continually sends updates to the Blinkin LED controller to keep the lights on - m_robotLED.setDefaultCommand(new UpdateLED(m_robotLED)); + m_robotLED.setDefaultCommand(new RunCommand(() -> m_robotLED.updateLED())); } /** @@ -58,11 +58,15 @@ public RobotContainer() { */ private void configureButtonBindings() { /* Driver Buttons */ - //Test command to spin the robot while pressing A on the driver controller + // test command to spin the robot while pressing A on the driver controller new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) .whileHeld(() -> m_robotDrive.driveWithInput(0, 1)); /* Operator Buttons */ + // activates "Lit Mode" + new JoystickButton(getOperatorJoystick(), XboxController.A_BUTTON) + .whenPressed(() -> m_robotLED.setPattern(LEDPatterns.LAVA_RAINBOW)) + .whenReleased(() -> m_robotLED.setPattern(LEDConstants.DEFAULT_PATTERN)); } /** diff --git a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java b/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java deleted file mode 100644 index a7e793e..0000000 --- a/src/main/java/frc4388/robot/commands/LED/SetLEDPattern.java +++ /dev/null @@ -1,37 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.commands.LED; - -import edu.wpi.first.wpilibj2.command.InstantCommand; - -import frc4388.robot.subsystems.LED; -import frc4388.utility.LEDPatterns; - -// NOTE: Consider using this command inline, rather than writing a subclass. For more -// information, see: -// https://docs.wpilib.org/en/latest/docs/software/commandbased/convenience-features.html -public class SetLEDPattern extends InstantCommand { - - private final LED m_led; - public static LEDPatterns m_pattern; - - /** - * Add your docs here. - */ - public SetLEDPattern(LED subsystem, LEDPatterns pattern) { - m_led = subsystem; - m_pattern = pattern; - addRequirements(m_led); - } - - // Called when the command is initially scheduled. - @Override - public void initialize() { - m_led.setPattern(m_pattern); - } -} diff --git a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java b/src/main/java/frc4388/robot/commands/LED/UpdateLED.java deleted file mode 100644 index 7baab4e..0000000 --- a/src/main/java/frc4388/robot/commands/LED/UpdateLED.java +++ /dev/null @@ -1,47 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.commands.LED; - -import edu.wpi.first.wpilibj2.command.CommandBase; - -import frc4388.robot.subsystems.LED; - -public class UpdateLED extends CommandBase { - - private final LED m_LED; - - /** - * Creates a new UpdateLED that continually runs updateLED in the LED subsystem. - */ - public UpdateLED(LED subsystem) { - m_LED = subsystem; - addRequirements(m_LED); - } - - // Called when the command is initially scheduled. - @Override - public void initialize() { - } - - // Called every time the scheduler runs while the command is scheduled. - @Override - public void execute() { - m_LED.updateLED(); - } - - // Called once the command ends or is interrupted. - @Override - public void end(boolean interrupted) { - } - - // Returns true when the command should end. - @Override - public boolean isFinished() { - return false; - } -} diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 1b9008c..a68c124 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -28,7 +28,7 @@ public class LED extends SubsystemBase { */ public LED(){ LEDController = new Spark(LEDConstants.LED_SPARK_ID); - setPattern(LEDPatterns.FOREST_WAVES); + setPattern(LEDConstants.DEFAULT_PATTERN); LEDController.set(currentLED); System.err.println("In the Beginning, there was Joe.\nAnd he said, 'Let there be LEDs.'\nAnd it was good."); } From b9bdb07ced1174454a940a8c1240c20e2425b73f Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 10 Jan 2020 21:30:36 -0700 Subject: [PATCH 23/70] Robot Work Now Default commands didn't have a required subsystem, causing the program to crash on launch. --- src/main/java/frc4388/robot/RobotContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 5421acf..1aaaddc 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -45,9 +45,9 @@ public RobotContainer() { // drives the robot with a two-axis input from the driver controller m_robotDrive.setDefaultCommand(new RunCommand(() -> m_robotDrive.driveWithInput( getDriverController().getLeftYAxis(), - getDriverController().getRightXAxis()))); + getDriverController().getRightXAxis()), m_robotDrive)); // continually sends updates to the Blinkin LED controller to keep the lights on - m_robotLED.setDefaultCommand(new RunCommand(() -> m_robotLED.updateLED())); + m_robotLED.setDefaultCommand(new RunCommand(() -> m_robotLED.updateLED(), m_robotLED)); } /** From ada44df0aa3376cf4c02cbdd3be6bc649d3b2bec Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Mon, 17 Feb 2020 10:45:36 -0700 Subject: [PATCH 24/70] Update LICENSE so FIRST doesn't sue us --- LICENSE | 41 ++++++++++++++++++++++------------------- build.gradle | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/LICENSE b/LICENSE index 30b2d1a..c714e13 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,24 @@ -MIT License +Copyright (c) 2009-2018 FIRST +All rights reserved. -Copyright (c) 2019 HFocus +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the FIRST nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THIS SOFTWARE IS PROVIDED BY FIRST AND CONTRIBUTORS``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6ae9dcf..02903dc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2020.1.2" + id "edu.wpi.first.GradleRIO" version "2020.2.2" } sourceCompatibility = JavaVersion.VERSION_11 From 252afa04da4fc3f0c2154eb2fb0f23080fd93878 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 08:51:17 -0600 Subject: [PATCH 25/70] Update WPILib version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 02903dc..434f06c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2020.2.2" + id "edu.wpi.first.GradleRIO" version "2020.3.2" } sourceCompatibility = JavaVersion.VERSION_11 From b77d594fce9b06e4989e598c73624378b7b7b23b Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 08:54:56 -0600 Subject: [PATCH 26/70] Remove static declaration where applicable Using these objects should instead be done by passing the needed subsystem reference in RobotContainer --- src/main/java/frc4388/robot/subsystems/Drive.java | 11 ++++++----- src/main/java/frc4388/robot/subsystems/LED.java | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index af19272..780ce83 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -23,12 +23,13 @@ public class Drive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. - public static WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); - public static WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); - public static WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); - public static WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); + public WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); + public WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); + public WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); + public WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); + public DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + - public static DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); /** * Add your docs here. diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index a68c124..e550623 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -20,8 +20,8 @@ */ public class LED extends SubsystemBase { - public static float currentLED; - public static Spark LEDController; + public float currentLED; + public Spark LEDController; /** * Add your docs here. From 2711743129a9f539c4d8b4f1d772c48ebc88c5c2 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 11:36:35 -0600 Subject: [PATCH 27/70] Create the RobotGyro Object Will allow for easily interchangable use of a pigeon or a navX. Just swap which gyro you pass RobotGyro. More advanced functionality like RemoteSensors with the pigeon and collision detection with the navX will have to be done by getting the gyro from RobotGyro and then using it. --- src/main/java/frc4388/robot/Constants.java | 4 +- .../java/frc4388/robot/subsystems/Drive.java | 7 +- src/main/java/frc4388/utility/RobotGyro.java | 158 ++++++++++++++++++ vendordeps/navx_frc.json | 35 ++++ 4 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/main/java/frc4388/utility/RobotGyro.java create mode 100644 vendordeps/navx_frc.json diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index 933908d..b948df1 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -22,7 +22,9 @@ public static final class DriveConstants { public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; public static final int DRIVE_LEFT_BACK_CAN_ID = 3; - public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; + public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; + + public static final int DRIVE_PIGEON_ID = 6; } public static final class LEDConstants { diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 780ce83..03ac015 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -10,11 +10,15 @@ import com.ctre.phoenix.motorcontrol.InvertType; import com.ctre.phoenix.motorcontrol.NeutralMode; import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; +import com.ctre.phoenix.sensors.PigeonIMU; +import com.kauailabs.navx.frc.AHRS; +import edu.wpi.first.wpilibj.GyroBase; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.DriveConstants; +import frc4388.utility.RobotGyro; /** * Add your docs here. @@ -28,8 +32,7 @@ public class Drive extends SubsystemBase { public WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); public WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); public DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); - - + public RobotGyro m_gyro = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); /** * Add your docs here. diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java new file mode 100644 index 0000000..40b08b7 --- /dev/null +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -0,0 +1,158 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import com.ctre.phoenix.sensors.PigeonIMU; +import com.ctre.phoenix.sensors.PigeonIMU.CalibrationMode; +import com.ctre.phoenix.sensors.PigeonIMU.PigeonState; +import com.kauailabs.navx.frc.AHRS; + +import edu.wpi.first.wpilibj.GyroBase; +import edu.wpi.first.wpiutil.math.MathUtil; + +/** + * Gyro class that allows for interchangeable use between a pigeon and a navX + */ +public class RobotGyro extends GyroBase { + private PigeonIMU m_pigeon; + private AHRS m_navX; + public boolean isGyroAPigeon; //true if pigeon, false if navX + + /** + * Creates a Gyro based on a pigeon + * @param gyro the gyroscope to use for Gyro + */ + public RobotGyro(PigeonIMU gyro) { + m_pigeon = gyro; + isGyroAPigeon = true; + } + + /** + * Creates a Gyro based on a navX + * @param gyro the gyroscope to use for Gyro + */ + public RobotGyro(AHRS gyro){ + m_navX = gyro; + isGyroAPigeon = false; + } + + /** + *

NavX: + *

Calibrate the gyro by running for a number of samples and computing the center value. Then use + * the center value as the Accumulator center value for subsequent measurements. It's important to + * make sure that the robot is not moving while the centering calculations are in progress, this + * is typically done when the robot is first turned on while it's sitting at rest before the + * competition starts. + * + *

Pigeon: + *

Calibrate the gyro by collecting data at a range of tempuratures. Allow pigeon to cool, then boot + * into calibration mode. For faster calibration, use a heat lamp to heat up the pigeon. Once the pigeon + * has seen a reasonable range of tempuratures, it will exit calibration mode. It's important to + * make sure that the robot is not moving while the tempurature calculations are in progress, this + * is typically done when the robot is first turned on while it's sitting at rest before the + * competition starts. + */ + @Override + public void calibrate() { + if (isGyroAPigeon) { + m_pigeon.enterCalibrationMode(CalibrationMode.Temperature); + } else { + m_navX.calibrate(); + } + } + + @Override + public void reset() { + if (isGyroAPigeon) { + m_pigeon.setYaw(0); + } else { + m_navX.reset(); + } + } + + /** + * Get Yaw, Pitch, and Roll data. + * + * @return ypr_deg Array with yaw[0], pitch[1], and roll[2] data. + * Yaw is within [-368,640, +368,640] degrees. + * Pitch is within [-90,+90] degrees. + * Roll is within [-90,+90] degrees. + */ + public double[] getPigeonAngles() { + double[] angles = new double[3]; + m_pigeon.getYawPitchRoll(angles); + return angles; + } + + @Override + public double getAngle() { + if (isGyroAPigeon) { + return getPigeonAngles()[0]; + } else { + return m_navX.getAngle(); + } + } + + /** + * Gets an absolute heading of the robot + * @return heading from -180 to 180 degrees + */ + public double getHeading() { + return Math.IEEEremainder(getAngle(), 360); + } + + /** + * Returns the current pitch value (in degrees, from -90 to 90) + * reported by the sensor. Pitch is a measure of rotation around + * the Y Axis. + * @return The current pitch value in degrees (-90 to 90). + */ + public double getPitch() { + if (isGyroAPigeon) { + return MathUtil.clamp(getPigeonAngles()[1], -90, 90); + } else { + return MathUtil.clamp(m_navX.getPitch(), -90, 90); + } + } + + /** + * Returns the current roll value (in degrees, from -90 to 90) + * reported by the sensor. Roll is a measure of rotation around + * the X Axis. + * @return The current roll value in degrees (-90 to 90). + */ + public double getRoll() { + if (isGyroAPigeon) { + return MathUtil.clamp(getPigeonAngles()[2], -90, 90); + } else { + return MathUtil.clamp(m_navX.getRoll(), -90, 90); + } + } + + @Override + public double getRate() { + if (isGyroAPigeon) { + return 0; + } else { + return m_navX.getRate(); + } + } + + public PigeonIMU getPigeon(){ + return m_pigeon; + } + + public AHRS getNavX(){ + return m_navX; + } + + @Override + public void close() throws Exception { + + } +} diff --git a/vendordeps/navx_frc.json b/vendordeps/navx_frc.json new file mode 100644 index 0000000..dca1d82 --- /dev/null +++ b/vendordeps/navx_frc.json @@ -0,0 +1,35 @@ +{ + "fileName": "navx_frc.json", + "name": "KauaiLabs_navX_FRC", + "version": "3.1.413", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "mavenUrls": [ + "https://repo1.maven.org/maven2/" + ], + "jsonUrl": "https://www.kauailabs.com/dist/frc/2020/navx_frc.json", + "javaDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-java", + "version": "3.1.413" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-cpp", + "version": "3.1.413", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": false, + "libName": "navx_frc", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxraspbian", + "windowsx86-64" + ] + } + ] +} \ No newline at end of file From 4c31d13e2e3b88ebf86eef3a1fc52b86e13788d1 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 12:20:22 -0600 Subject: [PATCH 28/70] Create RobotTime To keep track of deltas, frames, and times. Need it for completing pigeon gyro rates. --- src/main/java/frc4388/robot/Constants.java | 2 + src/main/java/frc4388/robot/Robot.java | 5 ++ .../java/frc4388/robot/subsystems/Drive.java | 18 +++++ src/main/java/frc4388/utility/RobotTime.java | 67 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 src/main/java/frc4388/utility/RobotTime.java diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index b948df1..d92d66b 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -25,6 +25,8 @@ public static final class DriveConstants { public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; public static final int DRIVE_PIGEON_ID = 6; + + public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; } public static final class LEDConstants { diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index 7735364..d2c3a6c 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -10,6 +10,7 @@ import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc4388.utility.RobotTime; /** * The VM is configured to automatically run this class, and to call the @@ -44,6 +45,7 @@ public void robotInit() { */ @Override public void robotPeriodic() { + RobotTime.updateTimes(); // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled // commands, running already-scheduled commands, removing finished or interrupted commands, // and running subsystem periodic() methods. This must be called from the robot's periodic @@ -58,6 +60,7 @@ public void robotPeriodic() { */ @Override public void disabledInit() { + RobotTime.endMatchTime(); } @Override @@ -82,6 +85,7 @@ public void autonomousInit() { if (m_autonomousCommand != null) { m_autonomousCommand.schedule(); } + RobotTime.startMatchTime(); } /** @@ -100,6 +104,7 @@ public void teleopInit() { if (m_autonomousCommand != null) { m_autonomousCommand.cancel(); } + RobotTime.startMatchTime(); } /** diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 03ac015..bec6e92 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -15,10 +15,12 @@ import edu.wpi.first.wpilibj.GyroBase; import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.DriveConstants; import frc4388.utility.RobotGyro; +import frc4388.utility.RobotTime; /** * Add your docs here. @@ -61,10 +63,26 @@ public Drive(){ m_rightBackMotor.setInverted(InvertType.FollowMaster); } + @Override + public void periodic() { + if (RobotTime.frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { + updateSmartDashboard(); + } + } + /** * Add your docs here. */ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } + + private void updateSmartDashboard() { + + // Examples of the functionality of RobotGyro + SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.isGyroAPigeon); + SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); + SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); + SmartDashboard.putData(m_gyro); + } } diff --git a/src/main/java/frc4388/utility/RobotTime.java b/src/main/java/frc4388/utility/RobotTime.java new file mode 100644 index 0000000..74020e6 --- /dev/null +++ b/src/main/java/frc4388/utility/RobotTime.java @@ -0,0 +1,67 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +/** + *

Keeps track of Robot times like time passed, delta time, etc + *

All times are in milliseconds + */ +public final class RobotTime { + private static long currTime = System.currentTimeMillis(); + public static long deltaTime = 0; + + private static long startRobotTime = currTime; + public static long robotTime = 0; + public static long lastRobotTime = 0; + + private static long startMatchTime = 0; + public static long matchTime = 0; + public static long lastMatchTime = 0; + + public static long frameNumber = 0; + + /** + * This class should not be instantiated. + */ + private RobotTime(){} + + /** + * Call this once per periodic loop. + */ + public static void updateTimes() { + lastRobotTime = robotTime; + lastMatchTime = matchTime; + + currTime = System.currentTimeMillis(); + robotTime = currTime - startRobotTime; + deltaTime = robotTime - lastRobotTime; + frameNumber++; + + if (matchTime != 0) { + matchTime = currTime - startMatchTime; + } + } + + /** + * Call this in both the auto and periodic inits + */ + public static void startMatchTime() { + if (matchTime == 0) { + startMatchTime = currTime; + matchTime = 1; + } + } + + /** + * Call this in disabled init + */ + public static void endMatchTime() { + startMatchTime = 0; + matchTime = 0; + } +} From 5e4544e7d3840ff00c1dd8ebecede6b873ca87ac Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 12:32:46 -0600 Subject: [PATCH 29/70] Add basic getRate() functionallity for Pigeons --- .../java/frc4388/robot/subsystems/Drive.java | 8 ++-- src/main/java/frc4388/utility/RobotGyro.java | 36 ++++++++++----- src/main/java/frc4388/utility/RobotTime.java | 44 +++++++++---------- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index bec6e92..a0444f3 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -11,9 +11,7 @@ import com.ctre.phoenix.motorcontrol.NeutralMode; import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; import com.ctre.phoenix.sensors.PigeonIMU; -import com.kauailabs.navx.frc.AHRS; -import edu.wpi.first.wpilibj.GyroBase; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; @@ -65,7 +63,9 @@ public Drive(){ @Override public void periodic() { - if (RobotTime.frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { + m_gyro.updatePigeonDeltas(); + + if (RobotTime.m_frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { updateSmartDashboard(); } } @@ -80,7 +80,7 @@ public void driveWithInput(double move, double steer){ private void updateSmartDashboard() { // Examples of the functionality of RobotGyro - SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.isGyroAPigeon); + SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); SmartDashboard.putData(m_gyro); diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index 40b08b7..76e1831 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -9,7 +9,6 @@ import com.ctre.phoenix.sensors.PigeonIMU; import com.ctre.phoenix.sensors.PigeonIMU.CalibrationMode; -import com.ctre.phoenix.sensors.PigeonIMU.PigeonState; import com.kauailabs.navx.frc.AHRS; import edu.wpi.first.wpilibj.GyroBase; @@ -21,7 +20,10 @@ public class RobotGyro extends GyroBase { private PigeonIMU m_pigeon; private AHRS m_navX; - public boolean isGyroAPigeon; //true if pigeon, false if navX + public boolean m_isGyroAPigeon; //true if pigeon, false if navX + + private double m_lastPigeonAngle; + private double m_deltaPigeonAngle; /** * Creates a Gyro based on a pigeon @@ -29,7 +31,7 @@ public class RobotGyro extends GyroBase { */ public RobotGyro(PigeonIMU gyro) { m_pigeon = gyro; - isGyroAPigeon = true; + m_isGyroAPigeon = true; } /** @@ -38,7 +40,17 @@ public RobotGyro(PigeonIMU gyro) { */ public RobotGyro(AHRS gyro){ m_navX = gyro; - isGyroAPigeon = false; + m_isGyroAPigeon = false; + } + + /** + * Run in periodic if you are using a pigeon. Updates a delta angle so that it can calculate getRate(). Note + * that the getRate() method for a navX will likely be much more accurate than for a pigeon. + */ + public void updatePigeonDeltas() { + double currentPigeonAngle = getAngle(); + m_deltaPigeonAngle = currentPigeonAngle - m_lastPigeonAngle; + m_lastPigeonAngle = currentPigeonAngle; } /** @@ -59,7 +71,7 @@ public RobotGyro(AHRS gyro){ */ @Override public void calibrate() { - if (isGyroAPigeon) { + if (m_isGyroAPigeon) { m_pigeon.enterCalibrationMode(CalibrationMode.Temperature); } else { m_navX.calibrate(); @@ -68,7 +80,7 @@ public void calibrate() { @Override public void reset() { - if (isGyroAPigeon) { + if (m_isGyroAPigeon) { m_pigeon.setYaw(0); } else { m_navX.reset(); @@ -83,7 +95,7 @@ public void reset() { * Pitch is within [-90,+90] degrees. * Roll is within [-90,+90] degrees. */ - public double[] getPigeonAngles() { + private double[] getPigeonAngles() { double[] angles = new double[3]; m_pigeon.getYawPitchRoll(angles); return angles; @@ -91,7 +103,7 @@ public double[] getPigeonAngles() { @Override public double getAngle() { - if (isGyroAPigeon) { + if (m_isGyroAPigeon) { return getPigeonAngles()[0]; } else { return m_navX.getAngle(); @@ -113,7 +125,7 @@ public double getHeading() { * @return The current pitch value in degrees (-90 to 90). */ public double getPitch() { - if (isGyroAPigeon) { + if (m_isGyroAPigeon) { return MathUtil.clamp(getPigeonAngles()[1], -90, 90); } else { return MathUtil.clamp(m_navX.getPitch(), -90, 90); @@ -127,7 +139,7 @@ public double getPitch() { * @return The current roll value in degrees (-90 to 90). */ public double getRoll() { - if (isGyroAPigeon) { + if (m_isGyroAPigeon) { return MathUtil.clamp(getPigeonAngles()[2], -90, 90); } else { return MathUtil.clamp(m_navX.getRoll(), -90, 90); @@ -136,8 +148,8 @@ public double getRoll() { @Override public double getRate() { - if (isGyroAPigeon) { - return 0; + if (m_isGyroAPigeon) { + return m_deltaPigeonAngle / (RobotTime.m_deltaTime * 1000); } else { return m_navX.getRate(); } diff --git a/src/main/java/frc4388/utility/RobotTime.java b/src/main/java/frc4388/utility/RobotTime.java index 74020e6..960366b 100644 --- a/src/main/java/frc4388/utility/RobotTime.java +++ b/src/main/java/frc4388/utility/RobotTime.java @@ -12,18 +12,18 @@ *

All times are in milliseconds */ public final class RobotTime { - private static long currTime = System.currentTimeMillis(); - public static long deltaTime = 0; + private static long m_currTime = System.currentTimeMillis(); + public static long m_deltaTime = 0; - private static long startRobotTime = currTime; - public static long robotTime = 0; - public static long lastRobotTime = 0; + private static long m_startRobotTime = m_currTime; + public static long m_robotTime = 0; + public static long m_lastRobotTime = 0; - private static long startMatchTime = 0; - public static long matchTime = 0; - public static long lastMatchTime = 0; + private static long m_startMatchTime = 0; + public static long m_matchTime = 0; + public static long m_lastMatchTime = 0; - public static long frameNumber = 0; + public static long m_frameNumber = 0; /** * This class should not be instantiated. @@ -34,16 +34,16 @@ private RobotTime(){} * Call this once per periodic loop. */ public static void updateTimes() { - lastRobotTime = robotTime; - lastMatchTime = matchTime; + m_lastRobotTime = m_robotTime; + m_lastMatchTime = m_matchTime; - currTime = System.currentTimeMillis(); - robotTime = currTime - startRobotTime; - deltaTime = robotTime - lastRobotTime; - frameNumber++; + m_currTime = System.currentTimeMillis(); + m_robotTime = m_currTime - m_startRobotTime; + m_deltaTime = m_robotTime - m_lastRobotTime; + m_frameNumber++; - if (matchTime != 0) { - matchTime = currTime - startMatchTime; + if (m_matchTime != 0) { + m_matchTime = m_currTime - m_startMatchTime; } } @@ -51,9 +51,9 @@ public static void updateTimes() { * Call this in both the auto and periodic inits */ public static void startMatchTime() { - if (matchTime == 0) { - startMatchTime = currTime; - matchTime = 1; + if (m_matchTime == 0) { + m_startMatchTime = m_currTime; + m_matchTime = 1; } } @@ -61,7 +61,7 @@ public static void startMatchTime() { * Call this in disabled init */ public static void endMatchTime() { - startMatchTime = 0; - matchTime = 0; + m_startMatchTime = 0; + m_matchTime = 0; } } From 7c8226989a65e1ac73b896357d9eaca848b9de27 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 13:42:20 -0600 Subject: [PATCH 30/70] Changed subsystem variables to private where applicable May need to be changed later, but try to use methods to access these variables instead of accessing them directly, as it fits better with the command based model and will be easier to rewrite should motors change, usage changes, etc. --- .../java/frc4388/robot/subsystems/Drive.java | 16 ++++++++++------ src/main/java/frc4388/robot/subsystems/LED.java | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index a0444f3..3323992 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -27,12 +27,12 @@ public class Drive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. - public WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); - public WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); - public WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); - public WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); - public DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); - public RobotGyro m_gyro = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); + private WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); + private WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); + private WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); + private WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); + private DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + private RobotGyro m_gyro = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); /** * Add your docs here. @@ -77,6 +77,10 @@ public void driveWithInput(double move, double steer){ m_driveTrain.arcadeDrive(move, steer); } + public RobotGyro getRobotGyro(){ + return m_gyro; + } + private void updateSmartDashboard() { // Examples of the functionality of RobotGyro diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index e550623..a2a6f1e 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -20,8 +20,8 @@ */ public class LED extends SubsystemBase { - public float currentLED; - public Spark LEDController; + private float currentLED; + private Spark LEDController; /** * Add your docs here. From 1389bfb95d32ca783bf36ac5a8502089a93bf890 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Fri, 27 Mar 2020 13:44:10 -0600 Subject: [PATCH 31/70] Example Usage of getRobotGyro If you need to access an object from outside the subsystem, you should do this. --- src/main/java/frc4388/robot/subsystems/Drive.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 3323992..6252eba 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -84,9 +84,9 @@ public RobotGyro getRobotGyro(){ private void updateSmartDashboard() { // Examples of the functionality of RobotGyro - SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); - SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); - SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); - SmartDashboard.putData(m_gyro); + SmartDashboard.putBoolean("Is Gyro a Pigeon?", getRobotGyro().m_isGyroAPigeon); + SmartDashboard.putNumber("Turn Rate", getRobotGyro().getRate()); + SmartDashboard.putNumber("Gyro Pitch", getRobotGyro().getPitch()); + SmartDashboard.putData(getRobotGyro()); } } From 997e52f94cf980e916b38111e1d44e164876efbe Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 21:27:15 -0600 Subject: [PATCH 32/70] Create RobotMap to decouple Subsystems from their hardware. With the hardware objects decoupled from the subsystems, we can then pass mock hardware into these subsystems to run simulations/tests on. --- .../java/frc4388/robot/RobotContainer.java | 48 +++++++------ src/main/java/frc4388/robot/RobotMap.java | 71 +++++++++++++++++++ .../java/frc4388/robot/subsystems/Drive.java | 63 ++++++---------- .../java/frc4388/robot/subsystems/LED.java | 26 +++---- 4 files changed, 132 insertions(+), 76 deletions(-) create mode 100644 src/main/java/frc4388/robot/RobotMap.java diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 1aaaddc..381910c 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -27,46 +27,51 @@ * commands, and button mappings) should be declared here. */ public class RobotContainer { + /* RobotMap */ + private final RobotMap m_robotMap = new RobotMap(); + /* Subsystems */ - private final Drive m_robotDrive = new Drive(); - private final LED m_robotLED = new LED(); + private final Drive m_robotDrive = new Drive(m_robotMap.leftFrontMotor, m_robotMap.rightFrontMotor, + m_robotMap.leftBackMotor, m_robotMap.rightBackMotor, m_robotMap.gyroDrive); + + private final LED m_robotLED = new LED(m_robotMap.LEDController); /* Controllers */ private final XboxController m_driverXbox = new XboxController(OIConstants.XBOX_DRIVER_ID); private final XboxController m_operatorXbox = new XboxController(OIConstants.XBOX_OPERATOR_ID); /** - * The container for the robot. Contains subsystems, OI devices, and commands. + * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { configureButtonBindings(); /* Default Commands */ // drives the robot with a two-axis input from the driver controller - m_robotDrive.setDefaultCommand(new RunCommand(() -> m_robotDrive.driveWithInput( - getDriverController().getLeftYAxis(), - getDriverController().getRightXAxis()), m_robotDrive)); + m_robotDrive.setDefaultCommand( + new RunCommand(() -> m_robotDrive.driveWithInput(getDriverController().getLeftYAxis(), + getDriverController().getRightXAxis()), m_robotDrive)); // continually sends updates to the Blinkin LED controller to keep the lights on m_robotLED.setDefaultCommand(new RunCommand(() -> m_robotLED.updateLED(), m_robotLED)); } /** - * Use this method to define your button->command mappings. Buttons can be created by - * instantiating a {@link GenericHID} or one of its subclasses ({@link - * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a - * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. - */ + * Use this method to define your button->command mappings. Buttons can be + * created by instantiating a {@link GenericHID} or one of its subclasses + * ({@link edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then + * passing it to a {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. + */ private void configureButtonBindings() { /* Driver Buttons */ // test command to spin the robot while pressing A on the driver controller new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) - .whileHeld(() -> m_robotDrive.driveWithInput(0, 1)); + .whileHeld(() -> m_robotDrive.driveWithInput(0, 1)); /* Operator Buttons */ // activates "Lit Mode" new JoystickButton(getOperatorJoystick(), XboxController.A_BUTTON) - .whenPressed(() -> m_robotLED.setPattern(LEDPatterns.LAVA_RAINBOW)) - .whenReleased(() -> m_robotLED.setPattern(LEDConstants.DEFAULT_PATTERN)); + .whenPressed(() -> m_robotLED.setPattern(LEDPatterns.LAVA_RAINBOW)) + .whenReleased(() -> m_robotLED.setPattern(LEDConstants.DEFAULT_PATTERN)); } /** @@ -85,28 +90,25 @@ public Command getAutonomousCommand() { public IHandController getDriverController() { return m_driverXbox; } - + /** * Add your docs here. */ - public IHandController getOperatorController() - { + public IHandController getOperatorController() { return m_operatorXbox; } - + /** * Add your docs here. */ - public Joystick getOperatorJoystick() - { + public Joystick getOperatorJoystick() { return m_operatorXbox.getJoyStick(); } - + /** * Add your docs here. */ - public Joystick getDriverJoystick() - { + public Joystick getDriverJoystick() { return m_driverXbox.getJoyStick(); } } diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java new file mode 100644 index 0000000..befd97a --- /dev/null +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -0,0 +1,71 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot; + +import com.ctre.phoenix.motorcontrol.InvertType; +import com.ctre.phoenix.motorcontrol.NeutralMode; +import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; +import com.ctre.phoenix.sensors.PigeonIMU; + +import edu.wpi.first.wpilibj.Spark; +import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import frc4388.robot.Constants.DriveConstants; +import frc4388.robot.Constants.LEDConstants; +import frc4388.utility.RobotGyro; + +/** + * Defines and holds all I/O objects on the Roborio. This is useful for unit + * testing and modularization. + */ +public class RobotMap { + + public RobotMap() { + configureLEDSubsystem(); + configureDriveSubsystem(); + } + + /* LED Subsystem */ + public final Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID); + + void configureLEDSubsystem() { + + } + + /* Drive Subsystem */ + public final WPI_TalonSRX leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); + public final WPI_TalonSRX rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); + public final WPI_TalonSRX leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); + public final WPI_TalonSRX rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); + public final DifferentialDrive driveTrain = new DifferentialDrive(leftFrontMotor, rightFrontMotor); + public final RobotGyro gyroDrive = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); + + void configureDriveSubsystem() { + + /* factory default values */ + leftFrontMotor.configFactoryDefault(); + rightFrontMotor.configFactoryDefault(); + leftBackMotor.configFactoryDefault(); + rightBackMotor.configFactoryDefault(); + + /* set back motors as followers */ + leftBackMotor.follow(leftFrontMotor); + rightBackMotor.follow(rightFrontMotor); + + /* set neutral mode */ + leftFrontMotor.setNeutralMode(NeutralMode.Brake); + rightFrontMotor.setNeutralMode(NeutralMode.Brake); + leftFrontMotor.setNeutralMode(NeutralMode.Brake); + rightFrontMotor.setNeutralMode(NeutralMode.Brake); + + /* flip input so forward becomes back, etc */ + leftFrontMotor.setInverted(false); + rightFrontMotor.setInverted(false); + leftBackMotor.setInverted(InvertType.FollowMaster); + rightBackMotor.setInverted(InvertType.FollowMaster); + } +} diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 6252eba..22870b0 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -7,10 +7,7 @@ package frc4388.robot.subsystems; -import com.ctre.phoenix.motorcontrol.InvertType; -import com.ctre.phoenix.motorcontrol.NeutralMode; import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; -import com.ctre.phoenix.sensors.PigeonIMU; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; @@ -27,38 +24,25 @@ public class Drive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. - private WPI_TalonSRX m_leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); - private WPI_TalonSRX m_rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); - private WPI_TalonSRX m_leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); - private WPI_TalonSRX m_rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); - private DifferentialDrive m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); - private RobotGyro m_gyro = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); + private WPI_TalonSRX m_leftFrontMotor; + private WPI_TalonSRX m_rightFrontMotor; + private WPI_TalonSRX m_leftBackMotor; + private WPI_TalonSRX m_rightBackMotor; + private DifferentialDrive m_driveTrain; + private RobotGyro m_gyro; /** * Add your docs here. */ - public Drive(){ - /* factory default values */ - m_leftFrontMotor.configFactoryDefault(); - m_rightFrontMotor.configFactoryDefault(); - m_leftBackMotor.configFactoryDefault(); - m_rightBackMotor.configFactoryDefault(); - - /* set back motors as followers */ - m_leftBackMotor.follow(m_leftFrontMotor); - m_rightBackMotor.follow(m_rightFrontMotor); - - /* set neutral mode */ - m_leftFrontMotor.setNeutralMode(NeutralMode.Brake); - m_rightFrontMotor.setNeutralMode(NeutralMode.Brake); - m_leftFrontMotor.setNeutralMode(NeutralMode.Brake); - m_rightFrontMotor.setNeutralMode(NeutralMode.Brake); - - /* flip input so forward becomes back, etc */ - m_leftFrontMotor.setInverted(false); - m_rightFrontMotor.setInverted(false); - m_leftBackMotor.setInverted(InvertType.FollowMaster); - m_rightBackMotor.setInverted(InvertType.FollowMaster); + public Drive(WPI_TalonSRX leftFrontMotor, WPI_TalonSRX rightFrontMotor, WPI_TalonSRX leftBackMotor, + WPI_TalonSRX rightBackMotor, RobotGyro gyro) { + + m_leftFrontMotor = leftFrontMotor; + m_rightFrontMotor = rightFrontMotor; + m_leftBackMotor = leftBackMotor; + m_rightBackMotor = rightBackMotor; + m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + m_gyro = gyro; } @Override @@ -73,20 +57,19 @@ public void periodic() { /** * Add your docs here. */ - public void driveWithInput(double move, double steer){ + public void driveWithInput(double move, double steer) { m_driveTrain.arcadeDrive(move, steer); } - public RobotGyro getRobotGyro(){ - return m_gyro; - } - + /** + * Add your docs here. + */ private void updateSmartDashboard() { // Examples of the functionality of RobotGyro - SmartDashboard.putBoolean("Is Gyro a Pigeon?", getRobotGyro().m_isGyroAPigeon); - SmartDashboard.putNumber("Turn Rate", getRobotGyro().getRate()); - SmartDashboard.putNumber("Gyro Pitch", getRobotGyro().getPitch()); - SmartDashboard.putData(getRobotGyro()); + SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); + SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); + SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); + SmartDashboard.putData(m_gyro); } } diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index a2a6f1e..c4cc39e 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -20,36 +20,36 @@ */ public class LED extends SubsystemBase { - private float currentLED; - private Spark LEDController; + private float m_currentLED; + private Spark m_LEDController; /** * Add your docs here. */ - public LED(){ - LEDController = new Spark(LEDConstants.LED_SPARK_ID); + public LED(Spark LEDController){ + m_LEDController = LEDController; setPattern(LEDConstants.DEFAULT_PATTERN); - LEDController.set(currentLED); + m_LEDController.set(m_currentLED); System.err.println("In the Beginning, there was Joe.\nAnd he said, 'Let there be LEDs.'\nAnd it was good."); } + @Override + public void periodic(){ + SmartDashboard.putNumber("LED", m_currentLED); + } + /** * Add your docs here. */ public void updateLED(){ - LEDController.set(currentLED); + m_LEDController.set(m_currentLED); } /** * Add your docs here. */ public void setPattern(LEDPatterns pattern){ - currentLED = pattern.getValue(); - LEDController.set(currentLED); - } - - @Override - public void periodic(){ - SmartDashboard.putNumber("LED", currentLED); + m_currentLED = pattern.getValue(); + m_LEDController.set(m_currentLED); } } \ No newline at end of file From 9602022379e4f9a750fec032c22fa43302670c60 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 21:42:08 -0600 Subject: [PATCH 33/70] Convert RobotTime into a singleton --- src/main/java/frc4388/robot/Robot.java | 11 ++--- src/main/java/frc4388/robot/RobotMap.java | 9 ++-- .../java/frc4388/robot/subsystems/Drive.java | 4 +- src/main/java/frc4388/utility/RobotGyro.java | 4 +- src/main/java/frc4388/utility/RobotTime.java | 41 ++++++++++++------- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index d2c3a6c..e145b38 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -21,7 +21,8 @@ */ public class Robot extends TimedRobot { Command m_autonomousCommand; - + + private RobotTime m_robotTime = RobotTime.getInstance(); private RobotContainer m_robotContainer; /** @@ -45,7 +46,7 @@ public void robotInit() { */ @Override public void robotPeriodic() { - RobotTime.updateTimes(); + m_robotTime.updateTimes(); // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled // commands, running already-scheduled commands, removing finished or interrupted commands, // and running subsystem periodic() methods. This must be called from the robot's periodic @@ -60,7 +61,7 @@ public void robotPeriodic() { */ @Override public void disabledInit() { - RobotTime.endMatchTime(); + m_robotTime.endMatchTime(); } @Override @@ -85,7 +86,7 @@ public void autonomousInit() { if (m_autonomousCommand != null) { m_autonomousCommand.schedule(); } - RobotTime.startMatchTime(); + m_robotTime.startMatchTime(); } /** @@ -104,7 +105,7 @@ public void teleopInit() { if (m_autonomousCommand != null) { m_autonomousCommand.cancel(); } - RobotTime.startMatchTime(); + m_robotTime.startMatchTime(); } /** diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index befd97a..952bc2b 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -25,15 +25,14 @@ public class RobotMap { public RobotMap() { - configureLEDSubsystem(); - configureDriveSubsystem(); + configureLEDMotorControllers(); + configureDriveMotorControllers(); } /* LED Subsystem */ public final Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID); - void configureLEDSubsystem() { - + void configureLEDMotorControllers() { } /* Drive Subsystem */ @@ -44,7 +43,7 @@ void configureLEDSubsystem() { public final DifferentialDrive driveTrain = new DifferentialDrive(leftFrontMotor, rightFrontMotor); public final RobotGyro gyroDrive = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); - void configureDriveSubsystem() { + void configureDriveMotorControllers() { /* factory default values */ leftFrontMotor.configFactoryDefault(); diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 22870b0..b32547c 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -24,6 +24,8 @@ public class Drive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. + private RobotTime m_robotTime = RobotTime.getInstance(); + private WPI_TalonSRX m_leftFrontMotor; private WPI_TalonSRX m_rightFrontMotor; private WPI_TalonSRX m_leftBackMotor; @@ -49,7 +51,7 @@ public Drive(WPI_TalonSRX leftFrontMotor, WPI_TalonSRX rightFrontMotor, WPI_Talo public void periodic() { m_gyro.updatePigeonDeltas(); - if (RobotTime.m_frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { + if (m_robotTime.m_frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { updateSmartDashboard(); } } diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index 76e1831..ab66d00 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -18,6 +18,8 @@ * Gyro class that allows for interchangeable use between a pigeon and a navX */ public class RobotGyro extends GyroBase { + private RobotTime m_robotTime = RobotTime.getInstance(); + private PigeonIMU m_pigeon; private AHRS m_navX; public boolean m_isGyroAPigeon; //true if pigeon, false if navX @@ -149,7 +151,7 @@ public double getRoll() { @Override public double getRate() { if (m_isGyroAPigeon) { - return m_deltaPigeonAngle / (RobotTime.m_deltaTime * 1000); + return m_deltaPigeonAngle / (m_robotTime.m_deltaTime * 1000); } else { return m_navX.getRate(); } diff --git a/src/main/java/frc4388/utility/RobotTime.java b/src/main/java/frc4388/utility/RobotTime.java index 960366b..d427fb7 100644 --- a/src/main/java/frc4388/utility/RobotTime.java +++ b/src/main/java/frc4388/utility/RobotTime.java @@ -11,29 +11,42 @@ *

Keeps track of Robot times like time passed, delta time, etc *

All times are in milliseconds */ -public final class RobotTime { - private static long m_currTime = System.currentTimeMillis(); - public static long m_deltaTime = 0; +public class RobotTime { + private long m_currTime = System.currentTimeMillis(); + public long m_deltaTime = 0; - private static long m_startRobotTime = m_currTime; - public static long m_robotTime = 0; - public static long m_lastRobotTime = 0; + private long m_startRobotTime = m_currTime; + public long m_robotTime = 0; + public long m_lastRobotTime = 0; - private static long m_startMatchTime = 0; - public static long m_matchTime = 0; - public static long m_lastMatchTime = 0; + private long m_startMatchTime = 0; + public long m_matchTime = 0; + public long m_lastMatchTime = 0; - public static long m_frameNumber = 0; + public long m_frameNumber = 0; /** - * This class should not be instantiated. + * Private constructor prevents other classes from instantiating */ private RobotTime(){} + private static RobotTime instance = null; + + /** + * Gets the instance of Robot Time. If there is no instance running one will be created. + * @return instance of Robot Time + */ + public static RobotTime getInstance() { + if (instance == null) { + instance = new RobotTime(); + } + return instance; + } + /** * Call this once per periodic loop. */ - public static void updateTimes() { + public void updateTimes() { m_lastRobotTime = m_robotTime; m_lastMatchTime = m_matchTime; @@ -50,7 +63,7 @@ public static void updateTimes() { /** * Call this in both the auto and periodic inits */ - public static void startMatchTime() { + public void startMatchTime() { if (m_matchTime == 0) { m_startMatchTime = m_currTime; m_matchTime = 1; @@ -60,7 +73,7 @@ public static void startMatchTime() { /** * Call this in disabled init */ - public static void endMatchTime() { + public void endMatchTime() { m_startMatchTime = 0; m_matchTime = 0; } From 59b9edd53dbd0f9e8fe169d92772991c83a6badd Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 22:45:44 -0600 Subject: [PATCH 34/70] Set up Mockito and simple Unit Test --- build.gradle | 1 + .../java/frc4388/robot/RobotContainer.java | 2 +- .../java/frc4388/robot/subsystems/Drive.java | 12 +++++- src/main/java/frc4388/utility/RobotGyro.java | 10 ++++- .../robot/utility/GyroHeadingTest.java | 41 +++++++++++++++++++ 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/test/java/frc4388/robot/utility/GyroHeadingTest.java diff --git a/build.gradle b/build.gradle index 434f06c..b202c6b 100644 --- a/build.gradle +++ b/build.gradle @@ -53,6 +53,7 @@ dependencies { nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) testImplementation 'junit:junit:4.12' + testCompile "org.mockito:mockito-core:2.+" // Enable simulation gui support. Must check the box in vscode to enable support // upon debugging diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 381910c..b7542bc 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -32,7 +32,7 @@ public class RobotContainer { /* Subsystems */ private final Drive m_robotDrive = new Drive(m_robotMap.leftFrontMotor, m_robotMap.rightFrontMotor, - m_robotMap.leftBackMotor, m_robotMap.rightBackMotor, m_robotMap.gyroDrive); + m_robotMap.leftBackMotor, m_robotMap.rightBackMotor, m_robotMap.driveTrain, m_robotMap.gyroDrive); private final LED m_robotLED = new LED(m_robotMap.LEDController); diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index b32547c..1703513 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -37,13 +37,13 @@ public class Drive extends SubsystemBase { * Add your docs here. */ public Drive(WPI_TalonSRX leftFrontMotor, WPI_TalonSRX rightFrontMotor, WPI_TalonSRX leftBackMotor, - WPI_TalonSRX rightBackMotor, RobotGyro gyro) { + WPI_TalonSRX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { m_leftFrontMotor = leftFrontMotor; m_rightFrontMotor = rightFrontMotor; m_leftBackMotor = leftBackMotor; m_rightBackMotor = rightBackMotor; - m_driveTrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); + m_driveTrain = driveTrain; m_gyro = gyro; } @@ -63,6 +63,14 @@ public void driveWithInput(double move, double steer) { m_driveTrain.arcadeDrive(move, steer); } + /** + * Add your docs here. + */ + public void tankDriveWithInput(double leftMove, double rightMove) { + m_leftFrontMotor.set(leftMove); + m_rightFrontMotor.set(rightMove); + } + /** * Add your docs here. */ diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index ab66d00..6d5ba5c 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -117,7 +117,15 @@ public double getAngle() { * @return heading from -180 to 180 degrees */ public double getHeading() { - return Math.IEEEremainder(getAngle(), 360); + return getHeading(getAngle()); + } + + /** + * Gets an absolute heading of the robot + * @return heading from -180 to 180 degrees + */ + public double getHeading(double angle) { + return Math.IEEEremainder(angle, 360); } /** diff --git a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java new file mode 100644 index 0000000..396a615 --- /dev/null +++ b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java @@ -0,0 +1,41 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.utility; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.ctre.phoenix.sensors.PigeonIMU; + +import edu.wpi.first.wpilibj.*; +import frc4388.utility.RobotGyro; + +import org.junit.*; + +/** + * Add your docs here. + */ +public class GyroHeadingTest { + + @Test + public void testConstructor() { + // Arrange + RobotGyro gyro = new RobotGyro(mock(PigeonIMU.class)); + + // Act & Assert + assertEquals(-90, gyro.getHeading(270), 0.0001); + assertEquals(-45, gyro.getHeading(315), 0.0001); + assertEquals(-60, gyro.getHeading(-60), 0.0001); + assertEquals(30, gyro.getHeading(30), 0.0001); + assertEquals(0, gyro.getHeading(0), 0.0001); + assertEquals(180, gyro.getHeading(180), 0.0001); + assertEquals(-180, gyro.getHeading(-180), 0.0001); + assertEquals(97, gyro.getHeading(1537), 0.0001); + assertEquals(99, gyro.getHeading(-2781), 0.0001); + } +} From 0de29b8277af3ade49cf2ac87757cfe66d52f831 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 22:47:55 -0600 Subject: [PATCH 35/70] Broken test for testing the CI --- src/test/java/frc4388/robot/utility/GyroHeadingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java index 396a615..69827a1 100644 --- a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java +++ b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java @@ -32,7 +32,7 @@ public void testConstructor() { assertEquals(-45, gyro.getHeading(315), 0.0001); assertEquals(-60, gyro.getHeading(-60), 0.0001); assertEquals(30, gyro.getHeading(30), 0.0001); - assertEquals(0, gyro.getHeading(0), 0.0001); + assertEquals(0, gyro.getHeading(1), 0.0001); assertEquals(180, gyro.getHeading(180), 0.0001); assertEquals(-180, gyro.getHeading(-180), 0.0001); assertEquals(97, gyro.getHeading(1537), 0.0001); From 5e81a4dd1e678aa09443545780c9101d481929db Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 22:49:32 -0600 Subject: [PATCH 36/70] Fix broken test --- src/test/java/frc4388/robot/utility/GyroHeadingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java index 69827a1..396a615 100644 --- a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java +++ b/src/test/java/frc4388/robot/utility/GyroHeadingTest.java @@ -32,7 +32,7 @@ public void testConstructor() { assertEquals(-45, gyro.getHeading(315), 0.0001); assertEquals(-60, gyro.getHeading(-60), 0.0001); assertEquals(30, gyro.getHeading(30), 0.0001); - assertEquals(0, gyro.getHeading(1), 0.0001); + assertEquals(0, gyro.getHeading(0), 0.0001); assertEquals(180, gyro.getHeading(180), 0.0001); assertEquals(-180, gyro.getHeading(-180), 0.0001); assertEquals(97, gyro.getHeading(1537), 0.0001); From 96ec916a27a1213b1fb7cc8c160e5318d399d8d5 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 23:15:45 -0600 Subject: [PATCH 37/70] Improve Gyro Heading Test --- .../RobotGyroUtilityTest.java} | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) rename src/test/java/frc4388/{robot/utility/GyroHeadingTest.java => utility/RobotGyroUtilityTest.java} (87%) diff --git a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java similarity index 87% rename from src/test/java/frc4388/robot/utility/GyroHeadingTest.java rename to src/test/java/frc4388/utility/RobotGyroUtilityTest.java index 396a615..74c589c 100644 --- a/src/test/java/frc4388/robot/utility/GyroHeadingTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -5,28 +5,25 @@ /* the project. */ /*----------------------------------------------------------------------------*/ -package frc4388.robot.utility; +package frc4388.utility; import static org.junit.Assert.*; import static org.mockito.Mockito.*; - -import com.ctre.phoenix.sensors.PigeonIMU; +import org.junit.*; import edu.wpi.first.wpilibj.*; +import com.ctre.phoenix.sensors.PigeonIMU; import frc4388.utility.RobotGyro; -import org.junit.*; - /** * Add your docs here. */ -public class GyroHeadingTest { +public class RobotGyroUtilityTest { + // Arrange + RobotGyro gyro = new RobotGyro(mock(PigeonIMU.class)); @Test - public void testConstructor() { - // Arrange - RobotGyro gyro = new RobotGyro(mock(PigeonIMU.class)); - + public void testHeading() { // Act & Assert assertEquals(-90, gyro.getHeading(270), 0.0001); assertEquals(-45, gyro.getHeading(315), 0.0001); From bdc6d91a074531b7a204a7b902acfa3f68ef3826 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 28 Mar 2020 23:16:04 -0600 Subject: [PATCH 38/70] Add LEDSubsystem Test --- .../java/frc4388/robot/subsystems/LED.java | 19 +++++--- .../robot/subsystems/LEDSubsystemTest.java | 43 +++++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index c4cc39e..a30b1b3 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -21,6 +21,7 @@ public class LED extends SubsystemBase { private float m_currentLED; + private LEDPatterns m_currentPattern; private Spark m_LEDController; /** @@ -29,27 +30,35 @@ public class LED extends SubsystemBase { public LED(Spark LEDController){ m_LEDController = LEDController; setPattern(LEDConstants.DEFAULT_PATTERN); - m_LEDController.set(m_currentLED); + updateLED(); System.err.println("In the Beginning, there was Joe.\nAnd he said, 'Let there be LEDs.'\nAnd it was good."); } @Override public void periodic(){ - SmartDashboard.putNumber("LED", m_currentLED); + SmartDashboard.putNumber("LED", m_currentPattern.getValue()); } /** * Add your docs here. */ public void updateLED(){ - m_LEDController.set(m_currentLED); + m_LEDController.set(m_currentPattern.getValue()); } /** * Add your docs here. */ public void setPattern(LEDPatterns pattern){ - m_currentLED = pattern.getValue(); - m_LEDController.set(m_currentLED); + m_currentPattern = pattern; + m_LEDController.set(m_currentPattern.getValue()); + } + + /** + * Add your docs here. + * @return + */ + public LEDPatterns getPattern() { + return m_currentPattern; } } \ No newline at end of file diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java new file mode 100644 index 0000000..c4ce771 --- /dev/null +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java @@ -0,0 +1,43 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.subsystems; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.*; +import edu.wpi.first.wpilibj.*; +import frc4388.robot.Constants.LEDConstants; +import frc4388.utility.LEDPatterns; + +/** + * Add your docs here. + */ +public class LEDSubsystemTest { + Spark ledController = mock(Spark.class); + LED led = new LED(ledController); + + @Test + public void testPatterns() { + assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + + led.setPattern(LEDPatterns.RAINBOW_RAINBOW); + assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); + + led.setPattern(LEDPatterns.BLUE_BREATH); + assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); + + led.setPattern(LEDPatterns.C1_SCANNER); + assertEquals(LEDPatterns.C1_SCANNER.getValue(), led.getPattern().getValue(), 0.0001); + + led.setPattern(LEDPatterns.C1_CHASE); + assertEquals(LEDPatterns.C1_CHASE.getValue(), led.getPattern().getValue(), 0.0001); + + led.setPattern(LEDPatterns.SOLID_BLACK); + assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); + } +} From 3a202911101feef4c6e4ea27b8c8eb692d0285c9 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 29 Mar 2020 00:10:06 -0600 Subject: [PATCH 39/70] Create Mock PigeonIMU and write more Unit tests - Test to make sure the RoboyGyro class knows what kind of gyro its using - Test to make sure all the getters for the gyro class work (at least for pigeon) --- .../java/frc4388/mocks/MockPigeonIMU.java | 54 ++++++++++++ src/main/java/frc4388/utility/RobotGyro.java | 4 +- .../robot/subsystems/LEDSubsystemTest.java | 6 -- .../frc4388/utility/RobotGyroUtilityTest.java | 83 ++++++++++++++++--- 4 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 src/main/java/frc4388/mocks/MockPigeonIMU.java diff --git a/src/main/java/frc4388/mocks/MockPigeonIMU.java b/src/main/java/frc4388/mocks/MockPigeonIMU.java new file mode 100644 index 0000000..ecddde7 --- /dev/null +++ b/src/main/java/frc4388/mocks/MockPigeonIMU.java @@ -0,0 +1,54 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.mocks; + +import com.ctre.phoenix.ErrorCode; +import com.ctre.phoenix.sensors.PigeonIMU; + +/** + * Add your docs here. + */ +public class MockPigeonIMU extends PigeonIMU { + public int m_deviceNumber; + public double currentYaw; + public double currentPitch; + public double currentRoll; + + public MockPigeonIMU(int deviceNumber) { + super(deviceNumber); + m_deviceNumber = deviceNumber; + } + + @Override + public ErrorCode setYaw(double angleDeg) { + currentYaw = angleDeg; + return ErrorCode.OK; + } + + /** + * @param currentPitch the Pitch to set + */ + public void setCurrentPitch(double currentPitch) { + this.currentPitch = currentPitch; + } + + /** + * @param currentRoll the Roll to set + */ + public void setCurrentRoll(double currentRoll) { + this.currentRoll = currentRoll; + } + + @Override + public ErrorCode getYawPitchRoll(double[] ypr_deg) { + ypr_deg[0] = currentYaw; + ypr_deg[1] = currentPitch; + ypr_deg[2] = currentRoll; + return ErrorCode.OK; + } +} diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index 6d5ba5c..d7d1adf 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -20,8 +20,8 @@ public class RobotGyro extends GyroBase { private RobotTime m_robotTime = RobotTime.getInstance(); - private PigeonIMU m_pigeon; - private AHRS m_navX; + private PigeonIMU m_pigeon = null; + private AHRS m_navX = null; public boolean m_isGyroAPigeon; //true if pigeon, false if navX private double m_lastPigeonAngle; diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java index c4ce771..14c1ce8 100644 --- a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java @@ -31,12 +31,6 @@ public void testPatterns() { led.setPattern(LEDPatterns.BLUE_BREATH); assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); - led.setPattern(LEDPatterns.C1_SCANNER); - assertEquals(LEDPatterns.C1_SCANNER.getValue(), led.getPattern().getValue(), 0.0001); - - led.setPattern(LEDPatterns.C1_CHASE); - assertEquals(LEDPatterns.C1_CHASE.getValue(), led.getPattern().getValue(), 0.0001); - led.setPattern(LEDPatterns.SOLID_BLACK); assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); } diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java index 74c589c..7bcc8c3 100644 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -13,26 +13,87 @@ import edu.wpi.first.wpilibj.*; import com.ctre.phoenix.sensors.PigeonIMU; +import com.kauailabs.navx.frc.AHRS; + +import frc4388.mocks.MockPigeonIMU; +import frc4388.robot.Constants.DriveConstants; import frc4388.utility.RobotGyro; /** * Add your docs here. */ public class RobotGyroUtilityTest { - // Arrange - RobotGyro gyro = new RobotGyro(mock(PigeonIMU.class)); + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + RobotGyro gyroPigeon = new RobotGyro(pigeon); + AHRS navX = mock(AHRS.class); + RobotGyro gyroNavX = new RobotGyro(navX); + + // TODO UNTESTED: calibrate(), getRate(), and most functions for NavX + + @Test + public void testConfig() { + assertEquals(true, gyroPigeon.m_isGyroAPigeon); + assertEquals(pigeon, gyroPigeon.getPigeon()); + assertEquals(null, gyroPigeon.getNavX()); + assertEquals(false, gyroNavX.m_isGyroAPigeon); + assertEquals(navX, gyroNavX.getNavX()); + assertEquals(null, gyroNavX.getPigeon()); + } @Test public void testHeading() { // Act & Assert - assertEquals(-90, gyro.getHeading(270), 0.0001); - assertEquals(-45, gyro.getHeading(315), 0.0001); - assertEquals(-60, gyro.getHeading(-60), 0.0001); - assertEquals(30, gyro.getHeading(30), 0.0001); - assertEquals(0, gyro.getHeading(0), 0.0001); - assertEquals(180, gyro.getHeading(180), 0.0001); - assertEquals(-180, gyro.getHeading(-180), 0.0001); - assertEquals(97, gyro.getHeading(1537), 0.0001); - assertEquals(99, gyro.getHeading(-2781), 0.0001); + assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); + assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); + assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); + assertEquals(30, gyroPigeon.getHeading(30), 0.0001); + assertEquals(0, gyroPigeon.getHeading(0), 0.0001); + assertEquals(180, gyroPigeon.getHeading(180), 0.0001); + assertEquals(-180, gyroPigeon.getHeading(-180), 0.0001); + assertEquals(97, gyroPigeon.getHeading(1537), 0.0001); + assertEquals(99, gyroPigeon.getHeading(-2781), 0.0001); + } + + @Test + public void testYawPitchRoll() { + assertEquals(0, gyroPigeon.getAngle(), 0.0001); + + pigeon.setYaw(40); + assertEquals(40, gyroPigeon.getAngle(), 0.0001); + + gyroPigeon.reset(); + assertEquals(0, gyroPigeon.getAngle(), 0.0001); + + pigeon.setYaw(-1457); + pigeon.setCurrentPitch(100); + pigeon.setCurrentRoll(100); + assertEquals(-1457, gyroPigeon.getAngle(), 0.0001); + assertEquals(90, gyroPigeon.getPitch(), 0.0001); + assertEquals(90, gyroPigeon.getRoll(), 0.0001); + + pigeon.setCurrentPitch(45); + pigeon.setCurrentRoll(45); + assertEquals(45, gyroPigeon.getPitch(), 0.0001); + assertEquals(45, gyroPigeon.getRoll(), 0.0001); + + pigeon.setCurrentPitch(0); + pigeon.setCurrentRoll(0); + assertEquals(0, gyroPigeon.getPitch(), 0.0001); + assertEquals(0, gyroPigeon.getRoll(), 0.0001); + + pigeon.setCurrentPitch(-60); + pigeon.setCurrentRoll(-60); + assertEquals(-60, gyroPigeon.getPitch(), 0.0001); + assertEquals(-60, gyroPigeon.getRoll(), 0.0001); + + pigeon.setCurrentPitch(-90); + pigeon.setCurrentRoll(-90); + assertEquals(-90, gyroPigeon.getPitch(), 0.0001); + assertEquals(-90, gyroPigeon.getRoll(), 0.0001); + + pigeon.setCurrentPitch(-100); + pigeon.setCurrentRoll(-100); + assertEquals(-90, gyroPigeon.getPitch(), 0.0001); + assertEquals(-90, gyroPigeon.getRoll(), 0.0001); } } From 2bb8afa7b484b1d9f04251847024dcfa0c53f434 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 29 Mar 2020 00:26:57 -0600 Subject: [PATCH 40/70] Cleanup Code --- src/main/java/frc4388/robot/subsystems/LED.java | 1 - src/{main => test}/java/frc4388/mocks/MockPigeonIMU.java | 0 2 files changed, 1 deletion(-) rename src/{main => test}/java/frc4388/mocks/MockPigeonIMU.java (100%) diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index a30b1b3..57c7b58 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -20,7 +20,6 @@ */ public class LED extends SubsystemBase { - private float m_currentLED; private LEDPatterns m_currentPattern; private Spark m_LEDController; diff --git a/src/main/java/frc4388/mocks/MockPigeonIMU.java b/src/test/java/frc4388/mocks/MockPigeonIMU.java similarity index 100% rename from src/main/java/frc4388/mocks/MockPigeonIMU.java rename to src/test/java/frc4388/mocks/MockPigeonIMU.java From b88b658dd905748e745ae7143101e2ff6b1c6f64 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Wed, 8 Apr 2020 09:30:16 -0600 Subject: [PATCH 41/70] Add tests for RobotTime class --- src/main/java/frc4388/utility/RobotTime.java | 5 +- .../frc4388/utility/RobotTimeUtilityTest.java | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 src/test/java/frc4388/utility/RobotTimeUtilityTest.java diff --git a/src/main/java/frc4388/utility/RobotTime.java b/src/main/java/frc4388/utility/RobotTime.java index d427fb7..694f850 100644 --- a/src/main/java/frc4388/utility/RobotTime.java +++ b/src/main/java/frc4388/utility/RobotTime.java @@ -55,7 +55,7 @@ public void updateTimes() { m_deltaTime = m_robotTime - m_lastRobotTime; m_frameNumber++; - if (m_matchTime != 0) { + if (m_startMatchTime != 0) { m_matchTime = m_currTime - m_startMatchTime; } } @@ -64,9 +64,8 @@ public void updateTimes() { * Call this in both the auto and periodic inits */ public void startMatchTime() { - if (m_matchTime == 0) { + if (m_startMatchTime == 0) { m_startMatchTime = m_currTime; - m_matchTime = 1; } } diff --git a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java new file mode 100644 index 0000000..96bf5b5 --- /dev/null +++ b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java @@ -0,0 +1,93 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.*; + +import edu.wpi.first.wpilibj.*; + +/** + * Add your docs here. + */ +public class RobotTimeUtilityTest { + RobotTime robotTime = RobotTime.getInstance(); + + @Test + public void testUpdateTimes() { + long lastTime; + + // Initialisation + assertEquals(0, robotTime.m_deltaTime); + assertEquals(0, robotTime.m_robotTime); + assertEquals(0, robotTime.m_lastRobotTime); + assertEquals(0, robotTime.m_frameNumber); + lastTime = robotTime.m_robotTime; + + wait(1); + robotTime.updateTimes(); + + // First Frame + assertEquals(true, robotTime.m_deltaTime > 0); + assertEquals(true, robotTime.m_robotTime > 0); + assertEquals(lastTime, robotTime.m_lastRobotTime); + assertEquals(1, robotTime.m_frameNumber); + lastTime = robotTime.m_robotTime; + + wait(1); + robotTime.updateTimes(); + + // Second Frame + assertEquals(true, robotTime.m_deltaTime > 0); + assertEquals(true, robotTime.m_robotTime > 0); + assertEquals(lastTime, robotTime.m_lastRobotTime); + assertEquals(2, robotTime.m_frameNumber); + } + + @Test + public void testMatchTime() { + long lastTime; + + // Second Frame + assertEquals(0, robotTime.m_matchTime); + assertEquals(0, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + robotTime.startMatchTime(); + wait(1); + robotTime.updateTimes(); + + // Third Frame + assertEquals(true, robotTime.m_matchTime > 0); + assertEquals(lastTime, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + wait(1); + robotTime.updateTimes(); + robotTime.endMatchTime(); + + // Fourth Frame + assertEquals(0, robotTime.m_matchTime); + assertEquals(lastTime, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + wait(1); + robotTime.updateTimes(); + + // Fifth Frame + assertEquals(0, robotTime.m_matchTime); + assertEquals(lastTime, robotTime.m_lastMatchTime); + } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } +} From c7a5c7cc354a281fa90bcc9df7ee20d00715a310 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Wed, 8 Apr 2020 10:27:33 -0600 Subject: [PATCH 42/70] Add tests for getRate() function in RobotGyro --- src/main/java/frc4388/robot/RobotMap.java | 1 + src/main/java/frc4388/utility/RobotGyro.java | 2 +- .../robot/subsystems/LEDSubsystemTest.java | 4 ++ .../frc4388/utility/RobotGyroUtilityTest.java | 62 ++++++++++++++++++- .../frc4388/utility/RobotTimeUtilityTest.java | 27 ++++---- 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 952bc2b..7efbfda 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -33,6 +33,7 @@ public RobotMap() { public final Spark LEDController = new Spark(LEDConstants.LED_SPARK_ID); void configureLEDMotorControllers() { + } /* Drive Subsystem */ diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index d7d1adf..a42e8c8 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -159,7 +159,7 @@ public double getRoll() { @Override public double getRate() { if (m_isGyroAPigeon) { - return m_deltaPigeonAngle / (m_robotTime.m_deltaTime * 1000); + return m_deltaPigeonAngle / m_robotTime.m_deltaTime * 1000; } else { return m_navX.getRate(); } diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java index 14c1ce8..3852edb 100644 --- a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java @@ -23,14 +23,18 @@ public class LEDSubsystemTest { @Test public void testPatterns() { + // TEST 1 assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + // TEST 2 led.setPattern(LEDPatterns.RAINBOW_RAINBOW); assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); + // TEST 3 led.setPattern(LEDPatterns.BLUE_BREATH); assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); + // TEST 4 led.setPattern(LEDPatterns.SOLID_BLACK); assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); } diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java index 7bcc8c3..7b5d5bb 100644 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -27,14 +27,18 @@ public class RobotGyroUtilityTest { RobotGyro gyroPigeon = new RobotGyro(pigeon); AHRS navX = mock(AHRS.class); RobotGyro gyroNavX = new RobotGyro(navX); + RobotTime robotTime = RobotTime.getInstance(); - // TODO UNTESTED: calibrate(), getRate(), and most functions for NavX + // TODO UNTESTED: most functions for NavX @Test public void testConfig() { + // TEST 1 assertEquals(true, gyroPigeon.m_isGyroAPigeon); assertEquals(pigeon, gyroPigeon.getPigeon()); assertEquals(null, gyroPigeon.getNavX()); + + // TEST 2 assertEquals(false, gyroNavX.m_isGyroAPigeon); assertEquals(navX, gyroNavX.getNavX()); assertEquals(null, gyroNavX.getPigeon()); @@ -42,7 +46,7 @@ public void testConfig() { @Test public void testHeading() { - // Act & Assert + // TESTS assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); @@ -55,15 +59,19 @@ public void testHeading() { } @Test - public void testYawPitchRoll() { + public void testYawPitchRoll() { + // TEST 1 assertEquals(0, gyroPigeon.getAngle(), 0.0001); + // TEST 2 pigeon.setYaw(40); assertEquals(40, gyroPigeon.getAngle(), 0.0001); + // TEST 3 gyroPigeon.reset(); assertEquals(0, gyroPigeon.getAngle(), 0.0001); + // TEST 4 pigeon.setYaw(-1457); pigeon.setCurrentPitch(100); pigeon.setCurrentRoll(100); @@ -71,29 +79,77 @@ public void testYawPitchRoll() { assertEquals(90, gyroPigeon.getPitch(), 0.0001); assertEquals(90, gyroPigeon.getRoll(), 0.0001); + // TEST 5 pigeon.setCurrentPitch(45); pigeon.setCurrentRoll(45); assertEquals(45, gyroPigeon.getPitch(), 0.0001); assertEquals(45, gyroPigeon.getRoll(), 0.0001); + // TEST 6 pigeon.setCurrentPitch(0); pigeon.setCurrentRoll(0); assertEquals(0, gyroPigeon.getPitch(), 0.0001); assertEquals(0, gyroPigeon.getRoll(), 0.0001); + // TEST 7 pigeon.setCurrentPitch(-60); pigeon.setCurrentRoll(-60); assertEquals(-60, gyroPigeon.getPitch(), 0.0001); assertEquals(-60, gyroPigeon.getRoll(), 0.0001); + // TEST 8 pigeon.setCurrentPitch(-90); pigeon.setCurrentRoll(-90); assertEquals(-90, gyroPigeon.getPitch(), 0.0001); assertEquals(-90, gyroPigeon.getRoll(), 0.0001); + // TEST 9 pigeon.setCurrentPitch(-100); pigeon.setCurrentRoll(-100); assertEquals(-90, gyroPigeon.getPitch(), 0.0001); assertEquals(-90, gyroPigeon.getRoll(), 0.0001); } + + @Test + public void testRates() { + // SETUP + pigeon.setYaw(0); + gyroPigeon.updatePigeonDeltas(); + + // TEST 1 + robotTime.m_deltaTime = 5; + pigeon.setYaw(0); + gyroPigeon.updatePigeonDeltas(); + assertEquals(0, gyroPigeon.getRate(), 1); + + // TEST 2 + robotTime.m_deltaTime = 5; + pigeon.setYaw(90); + gyroPigeon.updatePigeonDeltas(); + assertEquals(18000, gyroPigeon.getRate(), 0.001); + + // TEST 3 + robotTime.m_deltaTime = 5; + pigeon.setYaw(90); + gyroPigeon.updatePigeonDeltas(); + assertEquals(0, gyroPigeon.getRate(), 0.001); + + // TEST 4 + robotTime.m_deltaTime = 3; + pigeon.setYaw(-30); + gyroPigeon.updatePigeonDeltas(); + assertEquals(-40000, gyroPigeon.getRate(), 0.001); + + // TEST 5 + robotTime.m_deltaTime = 6; + pigeon.setYaw(690); + gyroPigeon.updatePigeonDeltas(); + assertEquals(120000, gyroPigeon.getRate(), 0.001); + } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } } diff --git a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java index 96bf5b5..c520321 100644 --- a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java @@ -21,29 +21,34 @@ public class RobotTimeUtilityTest { @Test public void testUpdateTimes() { + // SETUP long lastTime; + robotTime.m_deltaTime = 0; + robotTime.m_robotTime = 0; + robotTime.m_lastRobotTime = 0; + robotTime.m_frameNumber = 0; + robotTime.endMatchTime(); + robotTime.m_lastMatchTime = 0; - // Initialisation + // TEST 1 assertEquals(0, robotTime.m_deltaTime); assertEquals(0, robotTime.m_robotTime); assertEquals(0, robotTime.m_lastRobotTime); assertEquals(0, robotTime.m_frameNumber); lastTime = robotTime.m_robotTime; + // TEST 2 wait(1); robotTime.updateTimes(); - - // First Frame assertEquals(true, robotTime.m_deltaTime > 0); assertEquals(true, robotTime.m_robotTime > 0); assertEquals(lastTime, robotTime.m_lastRobotTime); assertEquals(1, robotTime.m_frameNumber); lastTime = robotTime.m_robotTime; + // TEST 3 wait(1); robotTime.updateTimes(); - - // Second Frame assertEquals(true, robotTime.m_deltaTime > 0); assertEquals(true, robotTime.m_robotTime > 0); assertEquals(lastTime, robotTime.m_lastRobotTime); @@ -52,35 +57,33 @@ public void testUpdateTimes() { @Test public void testMatchTime() { + // SETUP long lastTime; - // Second Frame + // TEST 1 assertEquals(0, robotTime.m_matchTime); assertEquals(0, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; + // TEST 2 robotTime.startMatchTime(); wait(1); robotTime.updateTimes(); - - // Third Frame assertEquals(true, robotTime.m_matchTime > 0); assertEquals(lastTime, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; + // TEST 3 wait(1); robotTime.updateTimes(); robotTime.endMatchTime(); - - // Fourth Frame assertEquals(0, robotTime.m_matchTime); assertEquals(lastTime, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; + // TEST 4 wait(1); robotTime.updateTimes(); - - // Fifth Frame assertEquals(0, robotTime.m_matchTime); assertEquals(lastTime, robotTime.m_lastMatchTime); } From 5ce3aa986c5982eec8cc2f80b796f68d3c1489ec Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 11:50:13 -0600 Subject: [PATCH 43/70] Add Templates Extension --- .templates/.editorconfig | 21 +++++++++++++ .templates/.gitignore | 29 +++++++++++++++++ .../template-sample-react-component/index.jsx | 12 +++++++ .../index.scss | 3 ++ .templates/template-sample/index.js | 5 +++ template.config.js | 31 +++++++++++++++++++ 6 files changed, 101 insertions(+) create mode 100644 .templates/.editorconfig create mode 100644 .templates/.gitignore create mode 100644 .templates/template-sample-react-component/index.jsx create mode 100644 .templates/template-sample-react-component/index.scss create mode 100644 .templates/template-sample/index.js create mode 100644 template.config.js diff --git a/.templates/.editorconfig b/.templates/.editorconfig new file mode 100644 index 0000000..689fb50 --- /dev/null +++ b/.templates/.editorconfig @@ -0,0 +1,21 @@ +# @see https://editorconfig-specification.readthedocs.io/en/latest/ + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab diff --git a/.templates/.gitignore b/.templates/.gitignore new file mode 100644 index 0000000..e748d61 --- /dev/null +++ b/.templates/.gitignore @@ -0,0 +1,29 @@ +# @see https://git-scm.com/docs/gitignore + +# `.DS_Store` is a file that stores custom attributes of its containing folder +.DS_Store + +# Logs +logs +*.log + +# Dependencies +node_modules +bower_components +vendor + +# Caches +.cache +.npm +.eslintcache + +# Temporaries +.tmp +.temp + +# Built +dist +target +built +output +out diff --git a/.templates/template-sample-react-component/index.jsx b/.templates/template-sample-react-component/index.jsx new file mode 100644 index 0000000..d52bbc7 --- /dev/null +++ b/.templates/template-sample-react-component/index.jsx @@ -0,0 +1,12 @@ +import React from "react"; +import classNames from "classnames/bind"; + +import styles from "./index.scss"; + +const cx = classNames.bind(styles); + +function __templateNameToPascalCase__() { + return

Hello :)
; +} + +export default __templateNameToPascalCase__; diff --git a/.templates/template-sample-react-component/index.scss b/.templates/template-sample-react-component/index.scss new file mode 100644 index 0000000..c2488a6 --- /dev/null +++ b/.templates/template-sample-react-component/index.scss @@ -0,0 +1,3 @@ +.__templateNameToParamCase__ { + display: inline-block; +} diff --git a/.templates/template-sample/index.js b/.templates/template-sample/index.js new file mode 100644 index 0000000..0c0ef38 --- /dev/null +++ b/.templates/template-sample/index.js @@ -0,0 +1,5 @@ +export default function __templateNameToPascalCase__() { + console.log("TemplateName -> __templateName__"); + console.log("TemplateName to ParamCase -> __templateNameToParamCase__"); + console.log("TemplateName to PascalCase -> __templateNameToPascalCase__"); +} diff --git a/template.config.js b/template.config.js new file mode 100644 index 0000000..54f2425 --- /dev/null +++ b/template.config.js @@ -0,0 +1,31 @@ +/** + * This file is a configuration file generated by the `Template` extension on `vscode` + * @see https://marketplace.visualstudio.com/items?itemName=yongwoo.template + */ +module.exports = { + // You can change the template path to another path + templateRootPath: "./.templates", + // After copying the template file the `replaceFileTextFn` function is executed + replaceFileTextFn: (fileText, templateName, utils) => { + // @see https://www.npmjs.com/package/change-case + const { changeCase } = utils; + // You can change the text in the file + return fileText + .replace(/__templateName__/gm, templateName) + .replace( + /__templateNameToPascalCase__/gm, + changeCase.pascalCase(templateName) + ) + .replace( + /__templateNameToParamCase__/gm, + changeCase.paramCase(templateName) + ); + }, + replaceFileNameFn: (fileName, templateName, utils) => { + const { path } = utils; + // @see https://nodejs.org/api/path.html#path_path_parse_path + const { base } = path.parse(fileName); + // You can change the file name + return base; + } +}; From 9d6a6d4d24c5ba0fe7a42b0cde1312737ef94a83 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 18:00:28 +0000 Subject: [PATCH 44/70] Update gradle.yml --- .github/workflows/gradle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 9eaf0d0..eda668d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,6 +5,7 @@ on: branches: - master pull_request: + types: [ready_for_review, opened, synchronize, reopened] branches: - master From 412f907991c3eeb0bdf6aefaaccf97967a1577cd Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 12:39:53 -0600 Subject: [PATCH 45/70] Add UtilityTest Template --- .templates/.editorconfig | 21 --------- .templates/.gitignore | 29 ------------ .templates/UtilityTest.java | 47 +++++++++++++++++++ .../template-sample-react-component/index.jsx | 12 ----- .../index.scss | 3 -- .templates/template-sample/index.js | 5 -- 6 files changed, 47 insertions(+), 70 deletions(-) delete mode 100644 .templates/.editorconfig delete mode 100644 .templates/.gitignore create mode 100644 .templates/UtilityTest.java delete mode 100644 .templates/template-sample-react-component/index.jsx delete mode 100644 .templates/template-sample-react-component/index.scss delete mode 100644 .templates/template-sample/index.js diff --git a/.templates/.editorconfig b/.templates/.editorconfig deleted file mode 100644 index 689fb50..0000000 --- a/.templates/.editorconfig +++ /dev/null @@ -1,21 +0,0 @@ -# @see https://editorconfig-specification.readthedocs.io/en/latest/ - -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -charset = utf-8 - -# 4 space indentation -[*.py] -indent_style = space -indent_size = 4 - -# Tab indentation (no size specified) -[Makefile] -indent_style = tab diff --git a/.templates/.gitignore b/.templates/.gitignore deleted file mode 100644 index e748d61..0000000 --- a/.templates/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# @see https://git-scm.com/docs/gitignore - -# `.DS_Store` is a file that stores custom attributes of its containing folder -.DS_Store - -# Logs -logs -*.log - -# Dependencies -node_modules -bower_components -vendor - -# Caches -.cache -.npm -.eslintcache - -# Temporaries -.tmp -.temp - -# Built -dist -target -built -output -out diff --git a/.templates/UtilityTest.java b/.templates/UtilityTest.java new file mode 100644 index 0000000..7dd40d0 --- /dev/null +++ b/.templates/UtilityTest.java @@ -0,0 +1,47 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.*; + +import edu.wpi.first.wpilibj.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Add your docs here. + */ +public class UtilityTest { + @Test + public void testExample() { + // Arrange + boolean isFalse = false; + int i = 0; + double d = 0.0; + + // Act + wait(1); + isFalse = !isFalse; + i++; + d -= Math.PI; + + // Assert + assertEquals(1, i); + assertEquals(-Math.PI, d, 0.001); + assertEquals(true, isFalse); + } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } +} diff --git a/.templates/template-sample-react-component/index.jsx b/.templates/template-sample-react-component/index.jsx deleted file mode 100644 index d52bbc7..0000000 --- a/.templates/template-sample-react-component/index.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import classNames from "classnames/bind"; - -import styles from "./index.scss"; - -const cx = classNames.bind(styles); - -function __templateNameToPascalCase__() { - return
Hello :)
; -} - -export default __templateNameToPascalCase__; diff --git a/.templates/template-sample-react-component/index.scss b/.templates/template-sample-react-component/index.scss deleted file mode 100644 index c2488a6..0000000 --- a/.templates/template-sample-react-component/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -.__templateNameToParamCase__ { - display: inline-block; -} diff --git a/.templates/template-sample/index.js b/.templates/template-sample/index.js deleted file mode 100644 index 0c0ef38..0000000 --- a/.templates/template-sample/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export default function __templateNameToPascalCase__() { - console.log("TemplateName -> __templateName__"); - console.log("TemplateName to ParamCase -> __templateNameToParamCase__"); - console.log("TemplateName to PascalCase -> __templateNameToPascalCase__"); -} From aeed0c5431dfc56456e2a8247e871b449327dce4 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 12:43:42 -0600 Subject: [PATCH 46/70] Add Subsystem Test Template --- .templates/SubsystemTest.java | 65 +++++++++++++++++++ .../robot/subsystems/LEDSubsystemTest.java | 36 ++++++++-- 2 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 .templates/SubsystemTest.java diff --git a/.templates/SubsystemTest.java b/.templates/SubsystemTest.java new file mode 100644 index 0000000..a138c61 --- /dev/null +++ b/.templates/SubsystemTest.java @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.*; + +import edu.wpi.first.wpilibj.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Add your docs here. + */ +public class UtilityTest { + @Test + public void testConstructor() { + // Arrange + Spark ledController = mock(Spark.class); + + // Act + LED led = new LED(ledController); + + // Assert + assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + } + + @Test + public void testPatterns() { + // Arrange + Spark ledController = mock(Spark.class); + LED led = new LED(ledController); + + // Act + led.setPattern(LEDPatterns.RAINBOW_RAINBOW); + + // Assert + assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); + + // Act + led.setPattern(LEDPatterns.BLUE_BREATH); + + // Assert + assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); + + // Act + led.setPattern(LEDPatterns.SOLID_BLACK); + + // Assert + assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); + } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } +} diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java index 3852edb..b1dd630 100644 --- a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java @@ -18,24 +18,46 @@ * Add your docs here. */ public class LEDSubsystemTest { - Spark ledController = mock(Spark.class); - LED led = new LED(ledController); + @Test + public void testConstructor() { + // Arrange + Spark ledController = mock(Spark.class); + + // Act + LED led = new LED(ledController); + + // Assert + assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + } @Test public void testPatterns() { - // TEST 1 - assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + // Arrange + Spark ledController = mock(Spark.class); + LED led = new LED(ledController); - // TEST 2 + // Act led.setPattern(LEDPatterns.RAINBOW_RAINBOW); + + // Assert assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); - // TEST 3 + // Act led.setPattern(LEDPatterns.BLUE_BREATH); + + // Assert assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); - // TEST 4 + // Act led.setPattern(LEDPatterns.SOLID_BLACK); + + // Assert assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } } From 2aaebe0f4e89eecf67b612ae8a8e749d8f4bad24 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 12:58:28 -0600 Subject: [PATCH 47/70] Restructure RobotGyroUtilityTest to be more modular --- .../frc4388/utility/RobotGyroUtilityTest.java | 96 +++++++++++++------ 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java index 7b5d5bb..b5957b6 100644 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -23,30 +23,36 @@ * Add your docs here. */ public class RobotGyroUtilityTest { - MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - RobotGyro gyroPigeon = new RobotGyro(pigeon); - AHRS navX = mock(AHRS.class); - RobotGyro gyroNavX = new RobotGyro(navX); - RobotTime robotTime = RobotTime.getInstance(); - // TODO UNTESTED: most functions for NavX @Test - public void testConfig() { - // TEST 1 + public void testConstructor() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + AHRS navX = mock(AHRS.class); + + // Act + RobotGyro gyroPigeon = new RobotGyro(pigeon); + RobotGyro gyroNavX = new RobotGyro(navX); + + // Assert 1 assertEquals(true, gyroPigeon.m_isGyroAPigeon); assertEquals(pigeon, gyroPigeon.getPigeon()); assertEquals(null, gyroPigeon.getNavX()); - // TEST 2 + // Assert 2 assertEquals(false, gyroNavX.m_isGyroAPigeon); assertEquals(navX, gyroNavX.getNavX()); assertEquals(null, gyroNavX.getPigeon()); } @Test - public void testHeading() { - // TESTS + public void testHeadingPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + RobotGyro gyroPigeon = new RobotGyro(pigeon); + + // Act & Assert assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); @@ -59,91 +65,123 @@ public void testHeading() { } @Test - public void testYawPitchRoll() { - // TEST 1 + public void testYawPitchRollPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + RobotGyro gyroPigeon = new RobotGyro(pigeon); + + // Assert assertEquals(0, gyroPigeon.getAngle(), 0.0001); - // TEST 2 + // Act pigeon.setYaw(40); + + // Assert assertEquals(40, gyroPigeon.getAngle(), 0.0001); - // TEST 3 + // Act gyroPigeon.reset(); + + // Assert assertEquals(0, gyroPigeon.getAngle(), 0.0001); - // TEST 4 + // Act pigeon.setYaw(-1457); pigeon.setCurrentPitch(100); pigeon.setCurrentRoll(100); + + // Assert assertEquals(-1457, gyroPigeon.getAngle(), 0.0001); assertEquals(90, gyroPigeon.getPitch(), 0.0001); assertEquals(90, gyroPigeon.getRoll(), 0.0001); - // TEST 5 + // Act pigeon.setCurrentPitch(45); pigeon.setCurrentRoll(45); + + // Assert assertEquals(45, gyroPigeon.getPitch(), 0.0001); assertEquals(45, gyroPigeon.getRoll(), 0.0001); - // TEST 6 + // Act pigeon.setCurrentPitch(0); pigeon.setCurrentRoll(0); + + // Assert assertEquals(0, gyroPigeon.getPitch(), 0.0001); assertEquals(0, gyroPigeon.getRoll(), 0.0001); - // TEST 7 + // Act pigeon.setCurrentPitch(-60); pigeon.setCurrentRoll(-60); + + // Assert assertEquals(-60, gyroPigeon.getPitch(), 0.0001); assertEquals(-60, gyroPigeon.getRoll(), 0.0001); - // TEST 8 + // Act pigeon.setCurrentPitch(-90); pigeon.setCurrentRoll(-90); + + // Assert assertEquals(-90, gyroPigeon.getPitch(), 0.0001); assertEquals(-90, gyroPigeon.getRoll(), 0.0001); - // TEST 9 + // Act pigeon.setCurrentPitch(-100); pigeon.setCurrentRoll(-100); + + // Assert assertEquals(-90, gyroPigeon.getPitch(), 0.0001); assertEquals(-90, gyroPigeon.getRoll(), 0.0001); } @Test - public void testRates() { - // SETUP - pigeon.setYaw(0); + public void testRatesPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + RobotGyro gyroPigeon = new RobotGyro(pigeon); + RobotTime robotTime = RobotTime.getInstance(); gyroPigeon.updatePigeonDeltas(); - // TEST 1 + // Act robotTime.m_deltaTime = 5; pigeon.setYaw(0); gyroPigeon.updatePigeonDeltas(); + + // Assert assertEquals(0, gyroPigeon.getRate(), 1); - // TEST 2 + // Act robotTime.m_deltaTime = 5; pigeon.setYaw(90); gyroPigeon.updatePigeonDeltas(); + + // Assert assertEquals(18000, gyroPigeon.getRate(), 0.001); - // TEST 3 + // Act robotTime.m_deltaTime = 5; pigeon.setYaw(90); gyroPigeon.updatePigeonDeltas(); + + // Assert assertEquals(0, gyroPigeon.getRate(), 0.001); - // TEST 4 + // Act robotTime.m_deltaTime = 3; pigeon.setYaw(-30); gyroPigeon.updatePigeonDeltas(); + + // Assert assertEquals(-40000, gyroPigeon.getRate(), 0.001); - // TEST 5 + // Act robotTime.m_deltaTime = 6; pigeon.setYaw(690); gyroPigeon.updatePigeonDeltas(); + + // Assert assertEquals(120000, gyroPigeon.getRate(), 0.001); } From 965ab6587bd659be1d28a44488c7e05648f035be Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 13:07:05 -0600 Subject: [PATCH 48/70] Extract RobotGyros to Prevent Resource Leak --- .../frc4388/utility/RobotGyroUtilityTest.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java index b5957b6..f7bbe67 100644 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -25,22 +25,21 @@ public class RobotGyroUtilityTest { // TODO UNTESTED: most functions for NavX + private RobotGyro gyroPigeon; + private RobotGyro gyroNavX; + @Test public void testConstructor() { // Arrange MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); AHRS navX = mock(AHRS.class); + gyroPigeon = new RobotGyro(pigeon); + gyroNavX = new RobotGyro(navX); - // Act - RobotGyro gyroPigeon = new RobotGyro(pigeon); - RobotGyro gyroNavX = new RobotGyro(navX); - - // Assert 1 + // Assert assertEquals(true, gyroPigeon.m_isGyroAPigeon); assertEquals(pigeon, gyroPigeon.getPigeon()); assertEquals(null, gyroPigeon.getNavX()); - - // Assert 2 assertEquals(false, gyroNavX.m_isGyroAPigeon); assertEquals(navX, gyroNavX.getNavX()); assertEquals(null, gyroNavX.getPigeon()); @@ -50,7 +49,7 @@ public void testConstructor() { public void testHeadingPigeon() { // Arrange MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - RobotGyro gyroPigeon = new RobotGyro(pigeon); + gyroPigeon = new RobotGyro(pigeon); // Act & Assert assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); @@ -68,7 +67,7 @@ public void testHeadingPigeon() { public void testYawPitchRollPigeon() { // Arrange MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - RobotGyro gyroPigeon = new RobotGyro(pigeon); + gyroPigeon = new RobotGyro(pigeon); // Assert assertEquals(0, gyroPigeon.getAngle(), 0.0001); @@ -140,7 +139,7 @@ public void testYawPitchRollPigeon() { public void testRatesPigeon() { // Arrange MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - RobotGyro gyroPigeon = new RobotGyro(pigeon); + gyroPigeon = new RobotGyro(pigeon); RobotTime robotTime = RobotTime.getInstance(); gyroPigeon.updatePigeonDeltas(); @@ -184,10 +183,4 @@ public void testRatesPigeon() { // Assert assertEquals(120000, gyroPigeon.getRate(), 0.001); } - - private void wait(int millis) { - try { - Thread.sleep(millis); - } catch (Exception e) {} - } } From e31c543351ed7a7826ef22cb110bb798194762a9 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 13:18:40 -0600 Subject: [PATCH 49/70] Cleanup code and improve Templates --- .templates/SubsystemTest.java | 20 +++---- .templates/UtilityTest.java | 58 ++++++++++++------- .../robot/subsystems/LEDSubsystemTest.java | 14 ++--- .../frc4388/utility/RobotGyroUtilityTest.java | 6 +- .../frc4388/utility/RobotTimeUtilityTest.java | 32 ++++++---- 5 files changed, 70 insertions(+), 60 deletions(-) diff --git a/.templates/SubsystemTest.java b/.templates/SubsystemTest.java index a138c61..51f30d5 100644 --- a/.templates/SubsystemTest.java +++ b/.templates/SubsystemTest.java @@ -5,21 +5,21 @@ /* the project. */ /*----------------------------------------------------------------------------*/ -package frc4388.utility; +package frc4388.robot.subsystems; import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import org.junit.*; -import edu.wpi.first.wpilibj.*; +import org.junit.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import edu.wpi.first.wpilibj.Spark; +import frc4388.robot.Constants.LEDConstants; +import frc4388.utility.LEDPatterns; /** - * Add your docs here. + * Based off the LEDSubsystemTest class */ -public class UtilityTest { +public class SubsystemTest { @Test public void testConstructor() { // Arrange @@ -56,10 +56,4 @@ public void testPatterns() { // Assert assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); } - - private void wait(int millis) { - try { - Thread.sleep(millis); - } catch (Exception e) {} - } } diff --git a/.templates/UtilityTest.java b/.templates/UtilityTest.java index 7dd40d0..11c15cd 100644 --- a/.templates/UtilityTest.java +++ b/.templates/UtilityTest.java @@ -9,39 +9,53 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import org.junit.*; -import edu.wpi.first.wpilibj.*; +import com.kauailabs.navx.frc.AHRS; + +import org.junit.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; +import frc4388.mocks.MockPigeonIMU; +import frc4388.robot.Constants.DriveConstants; /** - * Add your docs here. + * Based on the RobotGyroUtilityTest class */ public class UtilityTest { + private RobotGyro gyroPigeon; + private RobotGyro gyroNavX; + @Test - public void testExample() { + public void testConstructor() { // Arrange - boolean isFalse = false; - int i = 0; - double d = 0.0; - - // Act - wait(1); - isFalse = !isFalse; - i++; - d -= Math.PI; + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + AHRS navX = mock(AHRS.class); + gyroPigeon = new RobotGyro(pigeon); + gyroNavX = new RobotGyro(navX); // Assert - assertEquals(1, i); - assertEquals(-Math.PI, d, 0.001); - assertEquals(true, isFalse); + assertEquals(true, gyroPigeon.m_isGyroAPigeon); + assertEquals(pigeon, gyroPigeon.getPigeon()); + assertEquals(null, gyroPigeon.getNavX()); + assertEquals(false, gyroNavX.m_isGyroAPigeon); + assertEquals(navX, gyroNavX.getNavX()); + assertEquals(null, gyroNavX.getPigeon()); } - private void wait(int millis) { - try { - Thread.sleep(millis); - } catch (Exception e) {} + @Test + public void testHeadingPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + gyroPigeon = new RobotGyro(pigeon); + + // Act & Assert + assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); + assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); + assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); + assertEquals(30, gyroPigeon.getHeading(30), 0.0001); + assertEquals(0, gyroPigeon.getHeading(0), 0.0001); + assertEquals(180, gyroPigeon.getHeading(180), 0.0001); + assertEquals(-180, gyroPigeon.getHeading(-180), 0.0001); + assertEquals(97, gyroPigeon.getHeading(1537), 0.0001); + assertEquals(99, gyroPigeon.getHeading(-2781), 0.0001); } } diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java index b1dd630..8fcbf53 100644 --- a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java @@ -7,9 +7,11 @@ package frc4388.robot.subsystems; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; -import org.junit.*; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + import edu.wpi.first.wpilibj.*; import frc4388.robot.Constants.LEDConstants; import frc4388.utility.LEDPatterns; @@ -54,10 +56,4 @@ public void testPatterns() { // Assert assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); } - - private void wait(int millis) { - try { - Thread.sleep(millis); - } catch (Exception e) {} - } } diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java index f7bbe67..8c0b1e5 100644 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java @@ -9,15 +9,13 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import org.junit.*; -import edu.wpi.first.wpilibj.*; -import com.ctre.phoenix.sensors.PigeonIMU; import com.kauailabs.navx.frc.AHRS; +import org.junit.*; + import frc4388.mocks.MockPigeonIMU; import frc4388.robot.Constants.DriveConstants; -import frc4388.utility.RobotGyro; /** * Add your docs here. diff --git a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java index c520321..2c31a34 100644 --- a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java +++ b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java @@ -8,10 +8,8 @@ package frc4388.utility; import static org.junit.Assert.*; -import static org.mockito.Mockito.*; -import org.junit.*; -import edu.wpi.first.wpilibj.*; +import org.junit.*; /** * Add your docs here. @@ -21,7 +19,7 @@ public class RobotTimeUtilityTest { @Test public void testUpdateTimes() { - // SETUP + // Arrange long lastTime; robotTime.m_deltaTime = 0; robotTime.m_robotTime = 0; @@ -30,25 +28,29 @@ public void testUpdateTimes() { robotTime.endMatchTime(); robotTime.m_lastMatchTime = 0; - // TEST 1 + // Assert assertEquals(0, robotTime.m_deltaTime); assertEquals(0, robotTime.m_robotTime); assertEquals(0, robotTime.m_lastRobotTime); assertEquals(0, robotTime.m_frameNumber); lastTime = robotTime.m_robotTime; - // TEST 2 + // Act wait(1); robotTime.updateTimes(); + + // Assert assertEquals(true, robotTime.m_deltaTime > 0); assertEquals(true, robotTime.m_robotTime > 0); assertEquals(lastTime, robotTime.m_lastRobotTime); assertEquals(1, robotTime.m_frameNumber); lastTime = robotTime.m_robotTime; - // TEST 3 + // Act wait(1); robotTime.updateTimes(); + + // Assert assertEquals(true, robotTime.m_deltaTime > 0); assertEquals(true, robotTime.m_robotTime > 0); assertEquals(lastTime, robotTime.m_lastRobotTime); @@ -57,33 +59,39 @@ public void testUpdateTimes() { @Test public void testMatchTime() { - // SETUP + // Arrange long lastTime; - // TEST 1 + // Assert assertEquals(0, robotTime.m_matchTime); assertEquals(0, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; - // TEST 2 + // Act robotTime.startMatchTime(); wait(1); robotTime.updateTimes(); + + // Assert assertEquals(true, robotTime.m_matchTime > 0); assertEquals(lastTime, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; - // TEST 3 + // Act wait(1); robotTime.updateTimes(); robotTime.endMatchTime(); + + // Assert assertEquals(0, robotTime.m_matchTime); assertEquals(lastTime, robotTime.m_lastMatchTime); lastTime = robotTime.m_matchTime; - // TEST 4 + // Act wait(1); robotTime.updateTimes(); + + // Assert assertEquals(0, robotTime.m_matchTime); assertEquals(lastTime, robotTime.m_lastMatchTime); } From 7d357b074904d82de6d3ef238e175c5251f44d2a Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 14:02:37 -0600 Subject: [PATCH 50/70] Add CommandTest Template (Broken) --- .templates/CommandTest.java | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .templates/CommandTest.java diff --git a/.templates/CommandTest.java b/.templates/CommandTest.java new file mode 100644 index 0000000..72b31df --- /dev/null +++ b/.templates/CommandTest.java @@ -0,0 +1,40 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.commands; + +import edu.wpi.first.wpilibj2.command.*; +import frc4388.robot.subsystems.*; +import org.junit.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class CommandTest { + private CommandScheduler scheduler = null; + + @Before + public void setup() { + scheduler = CommandScheduler.getInstance(); + } + + // TODO: Update this to use an actual command. Won't work with inline commands for some reason + + @Test + public void testExample() { + // Arrange + Drive drive = mock(Drive.class); + RunCommand command = new RunCommand(() -> drive.driveWithInput(0, 0), drive); + + // Act + scheduler.schedule(command); + scheduler.run(); + + // Assert + verify(drive).driveWithInput(0, 0); + } +} From b8018699c8e426790b85a408de05246e20eed778 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sat, 11 Apr 2020 20:04:11 +0000 Subject: [PATCH 51/70] Update gradle.yml --- .github/workflows/gradle.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index eda668d..9eaf0d0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,7 +5,6 @@ on: branches: - master pull_request: - types: [ready_for_review, opened, synchronize, reopened] branches: - master From e7a81fb13e489e4d0d47b1135624d2c868e3efa6 Mon Sep 17 00:00:00 2001 From: "Keenan D. Buckley" Date: Sun, 12 Apr 2020 13:07:09 -0600 Subject: [PATCH 52/70] Switch from TalonSRX to TalonFx Drive Train --- src/main/java/frc4388/robot/RobotMap.java | 10 +++++----- src/main/java/frc4388/robot/subsystems/Drive.java | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 7efbfda..3456b71 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -9,7 +9,7 @@ import com.ctre.phoenix.motorcontrol.InvertType; import com.ctre.phoenix.motorcontrol.NeutralMode; -import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; +import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; import com.ctre.phoenix.sensors.PigeonIMU; import edu.wpi.first.wpilibj.Spark; @@ -37,10 +37,10 @@ void configureLEDMotorControllers() { } /* Drive Subsystem */ - public final WPI_TalonSRX leftFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); - public final WPI_TalonSRX rightFrontMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); - public final WPI_TalonSRX leftBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); - public final WPI_TalonSRX rightBackMotor = new WPI_TalonSRX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); + public final WPI_TalonFX leftFrontMotor = new WPI_TalonFX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); + public final WPI_TalonFX rightFrontMotor = new WPI_TalonFX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); + public final WPI_TalonFX leftBackMotor = new WPI_TalonFX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); + public final WPI_TalonFX rightBackMotor = new WPI_TalonFX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); public final DifferentialDrive driveTrain = new DifferentialDrive(leftFrontMotor, rightFrontMotor); public final RobotGyro gyroDrive = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java index 1703513..9bbdee7 100644 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ b/src/main/java/frc4388/robot/subsystems/Drive.java @@ -7,7 +7,7 @@ package frc4388.robot.subsystems; -import com.ctre.phoenix.motorcontrol.can.WPI_TalonSRX; +import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; @@ -26,18 +26,18 @@ public class Drive extends SubsystemBase { private RobotTime m_robotTime = RobotTime.getInstance(); - private WPI_TalonSRX m_leftFrontMotor; - private WPI_TalonSRX m_rightFrontMotor; - private WPI_TalonSRX m_leftBackMotor; - private WPI_TalonSRX m_rightBackMotor; + private WPI_TalonFX m_leftFrontMotor; + private WPI_TalonFX m_rightFrontMotor; + private WPI_TalonFX m_leftBackMotor; + private WPI_TalonFX m_rightBackMotor; private DifferentialDrive m_driveTrain; private RobotGyro m_gyro; /** * Add your docs here. */ - public Drive(WPI_TalonSRX leftFrontMotor, WPI_TalonSRX rightFrontMotor, WPI_TalonSRX leftBackMotor, - WPI_TalonSRX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { + public Drive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, + WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { m_leftFrontMotor = leftFrontMotor; m_rightFrontMotor = rightFrontMotor; From 3c68373054239ed679ccb824ac6be32c25c41c57 Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:21:14 -0600 Subject: [PATCH 53/70] Nuke the drive subsystem --- src/main/java/frc4388/robot/Constants.java | 10 --- .../java/frc4388/robot/RobotContainer.java | 9 -- src/main/java/frc4388/robot/RobotMap.java | 31 ------- .../java/frc4388/robot/subsystems/Drive.java | 85 ------------------- 4 files changed, 135 deletions(-) delete mode 100644 src/main/java/frc4388/robot/subsystems/Drive.java diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index d92d66b..2b03c61 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -18,16 +18,6 @@ * constants are needed, to reduce verbosity. */ public final class Constants { - public static final class DriveConstants { - public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; - public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; - public static final int DRIVE_LEFT_BACK_CAN_ID = 3; - public static final int DRIVE_RIGHT_BACK_CAN_ID = 5; - - public static final int DRIVE_PIGEON_ID = 6; - - public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; - } public static final class LEDConstants { public static final int LED_SPARK_ID = 0; diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index b7542bc..99f5cb1 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -13,7 +13,6 @@ import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; -import frc4388.robot.subsystems.Drive; import frc4388.robot.subsystems.LED; import frc4388.utility.LEDPatterns; import frc4388.utility.controller.IHandController; @@ -31,9 +30,6 @@ public class RobotContainer { private final RobotMap m_robotMap = new RobotMap(); /* Subsystems */ - private final Drive m_robotDrive = new Drive(m_robotMap.leftFrontMotor, m_robotMap.rightFrontMotor, - m_robotMap.leftBackMotor, m_robotMap.rightBackMotor, m_robotMap.driveTrain, m_robotMap.gyroDrive); - private final LED m_robotLED = new LED(m_robotMap.LEDController); /* Controllers */ @@ -48,9 +44,6 @@ public RobotContainer() { /* Default Commands */ // drives the robot with a two-axis input from the driver controller - m_robotDrive.setDefaultCommand( - new RunCommand(() -> m_robotDrive.driveWithInput(getDriverController().getLeftYAxis(), - getDriverController().getRightXAxis()), m_robotDrive)); // continually sends updates to the Blinkin LED controller to keep the lights on m_robotLED.setDefaultCommand(new RunCommand(() -> m_robotLED.updateLED(), m_robotLED)); } @@ -64,8 +57,6 @@ public RobotContainer() { private void configureButtonBindings() { /* Driver Buttons */ // test command to spin the robot while pressing A on the driver controller - new JoystickButton(getDriverJoystick(), XboxController.A_BUTTON) - .whileHeld(() -> m_robotDrive.driveWithInput(0, 1)); /* Operator Buttons */ // activates "Lit Mode" diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 3456b71..cd2ca95 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -36,36 +36,5 @@ void configureLEDMotorControllers() { } - /* Drive Subsystem */ - public final WPI_TalonFX leftFrontMotor = new WPI_TalonFX(DriveConstants.DRIVE_LEFT_FRONT_CAN_ID); - public final WPI_TalonFX rightFrontMotor = new WPI_TalonFX(DriveConstants.DRIVE_RIGHT_FRONT_CAN_ID); - public final WPI_TalonFX leftBackMotor = new WPI_TalonFX(DriveConstants.DRIVE_LEFT_BACK_CAN_ID); - public final WPI_TalonFX rightBackMotor = new WPI_TalonFX(DriveConstants.DRIVE_RIGHT_BACK_CAN_ID); - public final DifferentialDrive driveTrain = new DifferentialDrive(leftFrontMotor, rightFrontMotor); - public final RobotGyro gyroDrive = new RobotGyro(new PigeonIMU(DriveConstants.DRIVE_PIGEON_ID)); - void configureDriveMotorControllers() { - - /* factory default values */ - leftFrontMotor.configFactoryDefault(); - rightFrontMotor.configFactoryDefault(); - leftBackMotor.configFactoryDefault(); - rightBackMotor.configFactoryDefault(); - - /* set back motors as followers */ - leftBackMotor.follow(leftFrontMotor); - rightBackMotor.follow(rightFrontMotor); - - /* set neutral mode */ - leftFrontMotor.setNeutralMode(NeutralMode.Brake); - rightFrontMotor.setNeutralMode(NeutralMode.Brake); - leftFrontMotor.setNeutralMode(NeutralMode.Brake); - rightFrontMotor.setNeutralMode(NeutralMode.Brake); - - /* flip input so forward becomes back, etc */ - leftFrontMotor.setInverted(false); - rightFrontMotor.setInverted(false); - leftBackMotor.setInverted(InvertType.FollowMaster); - rightBackMotor.setInverted(InvertType.FollowMaster); - } } diff --git a/src/main/java/frc4388/robot/subsystems/Drive.java b/src/main/java/frc4388/robot/subsystems/Drive.java deleted file mode 100644 index 9bbdee7..0000000 --- a/src/main/java/frc4388/robot/subsystems/Drive.java +++ /dev/null @@ -1,85 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.subsystems; - -import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; - -import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj2.command.SubsystemBase; - -import frc4388.robot.Constants.DriveConstants; -import frc4388.utility.RobotGyro; -import frc4388.utility.RobotTime; - -/** - * Add your docs here. - */ -public class Drive extends SubsystemBase { - // Put methods for controlling this subsystem - // here. Call these from Commands. - - private RobotTime m_robotTime = RobotTime.getInstance(); - - private WPI_TalonFX m_leftFrontMotor; - private WPI_TalonFX m_rightFrontMotor; - private WPI_TalonFX m_leftBackMotor; - private WPI_TalonFX m_rightBackMotor; - private DifferentialDrive m_driveTrain; - private RobotGyro m_gyro; - - /** - * Add your docs here. - */ - public Drive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, - WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { - - m_leftFrontMotor = leftFrontMotor; - m_rightFrontMotor = rightFrontMotor; - m_leftBackMotor = leftBackMotor; - m_rightBackMotor = rightBackMotor; - m_driveTrain = driveTrain; - m_gyro = gyro; - } - - @Override - public void periodic() { - m_gyro.updatePigeonDeltas(); - - if (m_robotTime.m_frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { - updateSmartDashboard(); - } - } - - /** - * Add your docs here. - */ - public void driveWithInput(double move, double steer) { - m_driveTrain.arcadeDrive(move, steer); - } - - /** - * Add your docs here. - */ - public void tankDriveWithInput(double leftMove, double rightMove) { - m_leftFrontMotor.set(leftMove); - m_rightFrontMotor.set(rightMove); - } - - /** - * Add your docs here. - */ - private void updateSmartDashboard() { - - // Examples of the functionality of RobotGyro - SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); - SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); - SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); - SmartDashboard.putData(m_gyro); - } -} From 32914cd3db215e8e892c4ade82fbb453adf3d560 Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:24:48 -0600 Subject: [PATCH 54/70] fix bracket --- src/main/java/frc4388/robot/RobotMap.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index cd2ca95..610b642 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -37,4 +37,5 @@ void configureLEDMotorControllers() { } void configureDriveMotorControllers() { + } } From a83d26749c30e55f6897c895ab8c6fe2a1bd00b8 Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:34:58 -0600 Subject: [PATCH 55/70] fix importing somthing that doesn't exist --- src/main/java/frc4388/robot/RobotMap.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 610b642..4bec615 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -14,7 +14,6 @@ import edu.wpi.first.wpilibj.Spark; import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import frc4388.robot.Constants.DriveConstants; import frc4388.robot.Constants.LEDConstants; import frc4388.utility.RobotGyro; From 97358345dc9bf62b75a9520d51b5f75f92c5016b Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:40:23 -0600 Subject: [PATCH 56/70] realize we need DriveConstants with a Pigeon id --- src/main/java/frc4388/robot/Constants.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index 2b03c61..d50cd10 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -18,7 +18,12 @@ * constants are needed, to reduce verbosity. */ public final class Constants { + public static final class DriveConstants { + public static final int DRIVE_PIGEON_ID = 6; + public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; + } + public static final class LEDConstants { public static final int LED_SPARK_ID = 0; From 18273f4ec312c6a00fcc389ba91363f8f314e93e Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:38:27 -0600 Subject: [PATCH 57/70] DifferentalDrive.java --- .../robot/subsystems/DifferentialDrive.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/main/java/frc4388/robot/subsystems/DifferentialDrive.java diff --git a/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java b/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java new file mode 100644 index 0000000..9e849af --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java @@ -0,0 +1,85 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.subsystems; + +import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; + +import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +import frc4388.robot.Constants.DriveConstants; +import frc4388.utility.RobotGyro; +import frc4388.utility.RobotTime; + +/** + * Add your docs here. + */ +public class DifferentialDrive extends SubsystemBase { + // Put methods for controlling this subsystem + // here. Call these from Commands. + + private RobotTime m_robotTime = RobotTime.getInstance(); + + private WPI_TalonFX m_leftFrontMotor; + private WPI_TalonFX m_rightFrontMotor; + private WPI_TalonFX m_leftBackMotor; + private WPI_TalonFX m_rightBackMotor; + private DifferentialDrive m_driveTrain; + private RobotGyro m_gyro; + + /** + * Add your docs here. + */ + public Drive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, + WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { + + m_leftFrontMotor = leftFrontMotor; + m_rightFrontMotor = rightFrontMotor; + m_leftBackMotor = leftBackMotor; + m_rightBackMotor = rightBackMotor; + m_driveTrain = driveTrain; + m_gyro = gyro; + } + + @Override + public void periodic() { + m_gyro.updatePigeonDeltas(); + + if (m_robotTime.m_frameNumber % DriveConstants.SMARTDASHBOARD_UPDATE_FRAME == 0) { + updateSmartDashboard(); + } + } + + /** + * Add your docs here. + */ + public void driveWithInput(double move, double steer) { + m_driveTrain.arcadeDrive(move, steer); + } + + /** + * Add your docs here. + */ + public void tankDriveWithInput(double leftMove, double rightMove) { + m_leftFrontMotor.set(leftMove); + m_rightFrontMotor.set(rightMove); + } + + /** + * Add your docs here. + */ + private void updateSmartDashboard() { + + // Examples of the functionality of RobotGyro + SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); + SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); + SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); + SmartDashboard.putData(m_gyro); + } +} From f2ba9155806e6e035b343b04996affcb75d33d77 Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:43:18 -0600 Subject: [PATCH 58/70] DifferentalDrive.java II --- src/main/java/frc4388/robot/subsystems/DifferentialDrive.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java b/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java index 9e849af..21c4083 100644 --- a/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java +++ b/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java @@ -36,7 +36,7 @@ public class DifferentialDrive extends SubsystemBase { /** * Add your docs here. */ - public Drive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, + public DifferentialDrive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { m_leftFrontMotor = leftFrontMotor; From 1cf75ebfdde9c5340ad6b7be3b47a68976b45ae7 Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:48:00 -0600 Subject: [PATCH 59/70] DifDrive.java --- .../subsystems/{DifferentialDrive.java => DifDrive.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/frc4388/robot/subsystems/{DifferentialDrive.java => DifDrive.java} (93%) diff --git a/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java b/src/main/java/frc4388/robot/subsystems/DifDrive.java similarity index 93% rename from src/main/java/frc4388/robot/subsystems/DifferentialDrive.java rename to src/main/java/frc4388/robot/subsystems/DifDrive.java index 21c4083..c0b35fe 100644 --- a/src/main/java/frc4388/robot/subsystems/DifferentialDrive.java +++ b/src/main/java/frc4388/robot/subsystems/DifDrive.java @@ -20,7 +20,7 @@ /** * Add your docs here. */ -public class DifferentialDrive extends SubsystemBase { +public class DifDrive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. @@ -36,7 +36,7 @@ public class DifferentialDrive extends SubsystemBase { /** * Add your docs here. */ - public DifferentialDrive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, + public DifDrive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { m_leftFrontMotor = leftFrontMotor; From 5c12519d632cb9623e789c57e9e040a2b1bef6ad Mon Sep 17 00:00:00 2001 From: C4llSqin <83995467+C4llSqin@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:15:52 -0600 Subject: [PATCH 60/70] DiffDrive.java --- .../robot/subsystems/{DifDrive.java => DiffDrive.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/frc4388/robot/subsystems/{DifDrive.java => DiffDrive.java} (94%) diff --git a/src/main/java/frc4388/robot/subsystems/DifDrive.java b/src/main/java/frc4388/robot/subsystems/DiffDrive.java similarity index 94% rename from src/main/java/frc4388/robot/subsystems/DifDrive.java rename to src/main/java/frc4388/robot/subsystems/DiffDrive.java index c0b35fe..a791913 100644 --- a/src/main/java/frc4388/robot/subsystems/DifDrive.java +++ b/src/main/java/frc4388/robot/subsystems/DiffDrive.java @@ -20,7 +20,7 @@ /** * Add your docs here. */ -public class DifDrive extends SubsystemBase { +public class DiffDrive extends SubsystemBase { // Put methods for controlling this subsystem // here. Call these from Commands. @@ -36,7 +36,7 @@ public class DifDrive extends SubsystemBase { /** * Add your docs here. */ - public DifDrive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, + public DiffDrive(WPI_TalonFX leftFrontMotor, WPI_TalonFX rightFrontMotor, WPI_TalonFX leftBackMotor, WPI_TalonFX rightBackMotor, DifferentialDrive driveTrain, RobotGyro gyro) { m_leftFrontMotor = leftFrontMotor; From 416d93dc1ec002606100808f27437f1d9534b954 Mon Sep 17 00:00:00 2001 From: Aarav Shah Date: Tue, 1 Nov 2022 17:32:01 -0600 Subject: [PATCH 61/70] Create RobotUnits.java --- src/main/java/frc4388/utility/RobotUnits.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/frc4388/utility/RobotUnits.java diff --git a/src/main/java/frc4388/utility/RobotUnits.java b/src/main/java/frc4388/utility/RobotUnits.java new file mode 100644 index 0000000..9e07312 --- /dev/null +++ b/src/main/java/frc4388/utility/RobotUnits.java @@ -0,0 +1,27 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.utility; + +/** Aarav's good units class (better than WPILib) + * @author Aarav Shah */ + +public class RobotUnits { + // constants + + // angle conversions + public static double degreesToRadians(final double degrees) {return degrees * Math.PI / 180;} + + public static double radiansToDegrees(final double radians) {return radians / Math.PI * 180;} + + // falcon conversions + public static double falconTicksToRotations(final double ticks) {return ticks / 2048;} + + public static double falconRotationsToTicks(final double rotations) {return rotations * 2048;} + + // distance conversions + public static double metersToFeet(final double meters) {return meters * 3.28084;} + + public static double feetToMeters(final double feet) {return feet / 3.28084;} +} \ No newline at end of file From d3615f272b2513979dbde681512edc2c50bd6be8 Mon Sep 17 00:00:00 2001 From: 66945 <54561572+66945@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:43:01 -0600 Subject: [PATCH 62/70] Churro --- .idea/.gitignore | 3 + .idea/gradle.xml | 17 + .idea/misc.xml | 4 + .idea/vcs.xml | 6 + src/main/java/frc4388/robot/Constants.java | 46 +++ .../frc4388/robot/subsystems/SwerveDrive.java | 315 ++++++++++++++++++ .../robot/subsystems/SwerveModule.java | 181 ++++++++++ 7 files changed, 572 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 src/main/java/frc4388/robot/subsystems/SwerveDrive.java create mode 100644 src/main/java/frc4388/robot/subsystems/SwerveModule.java diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..4edc9d5 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6ed36dd --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index d92d66b..84bd57a 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -18,6 +18,52 @@ * constants are needed, to reduce verbosity. */ public final class Constants { + public static final class SwerveDriveConstants { + public static final double ROTATION_SPEED = 0.1; + public static final double WHEEL_SPEED = 0.1; + public static final double WIDTH = 22; + public static final double HEIGHT = 22; + public static final double JOYSTICK_TO_METERS_PER_SECOND = 5; + public static final double MAX_SPEED_FEET_PER_SEC = 16; + public static final double SPEED_FEET_PER_SECOND_AT_FULL_POWER = 20; + public static final int LEFT_FRONT_STEER_CAN_ID = 2; + public static final int LEFT_FRONT_WHEEL_CAN_ID = 3; + public static final int RIGHT_FRONT_STEER_CAN_ID = 4; + public static final int RIGHT_FRONT_WHEEL_CAN_ID = 5; + public static final int LEFT_BACK_STEER_CAN_ID = 6; + public static final int LEFT_BACK_WHEEL_CAN_ID = 7; + public static final int RIGHT_BACK_STEER_CAN_ID = 8; + public static final int RIGHT_BACK_WHEEL_CAN_ID = 9; + public static final int LEFT_FRONT_STEER_CAN_ENCODER_ID = 10; + public static final int RIGHT_FRONT_STEER_CAN_ENCODER_ID = 11; + public static final int LEFT_BACK_STEER_CAN_ENCODER_ID = 12; + public static final int RIGHT_BACK_STEER_CAN_ENCODER_ID = 13; + // ofsets are in degrees + //ofsets are in degrees + // public static final double LEFT_FRONT_ENCODER_OFFSET = 181.494141; + // public static final double RIGHT_FRONT_ENCODER_OFFSET = 360. - 59.238281; + // public static final double LEFT_BACK_ENCODER_OFFSET = 360. - 128.144531; + // public static final double RIGHT_BACK_ENCODER_OFFSET = 0.933594; + public static final double LEFT_FRONT_ENCODER_OFFSET = 180.0; + public static final double RIGHT_FRONT_ENCODER_OFFSET = 300.0; + public static final double LEFT_BACK_ENCODER_OFFSET = 360.0 - 128.0; + public static final double RIGHT_BACK_ENCODER_OFFSET = 0.0; + + // swerve PID constants + public static final int SWERVE_SLOT_IDX = 0; + public static final int SWERVE_PID_LOOP_IDX = 1; + public static final int SWERVE_TIMEOUT_MS = 30; + public static final Gains SWERVE_GAINS = new Gains(1.0, 0.0, 0.0, 0.0, 0, 1.0); + + // swerve configuration + public static final double NEUTRAL_DEADBAND = 0.04; + public static final double OPEN_LOOP_RAMP_RATE = 0.2; + public static final int REMOTE_0 = 0; + + // misc + public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; + } + public static final class DriveConstants { public static final int DRIVE_LEFT_FRONT_CAN_ID = 2; public static final int DRIVE_RIGHT_FRONT_CAN_ID = 4; diff --git a/src/main/java/frc4388/robot/subsystems/SwerveDrive.java b/src/main/java/frc4388/robot/subsystems/SwerveDrive.java new file mode 100644 index 0000000..34687d9 --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/SwerveDrive.java @@ -0,0 +1,315 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.subsystems; + +import com.ctre.phoenix.sensors.WPI_Pigeon2; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveDriveOdometry; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.smartdashboard.Field2d; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc4388.robot.Constants.OIConstants; +import frc4388.robot.Constants.SwerveDriveConstants; +import frc4388.utility.Gains; + +public class SwerveDrive extends SubsystemBase { + + private SwerveModule m_leftFront; + private SwerveModule m_leftBack; + private SwerveModule m_rightFront; + private SwerveModule m_rightBack; + + double halfWidth = SwerveDriveConstants.WIDTH / 2.d; + double halfHeight = SwerveDriveConstants.HEIGHT / 2.d; + + public static Gains m_swerveGains = SwerveDriveConstants.SWERVE_GAINS; + + Translation2d m_frontLeftLocation = + new Translation2d( + Units.inchesToMeters(halfHeight), + Units.inchesToMeters(halfWidth)); + Translation2d m_frontRightLocation = + new Translation2d( + Units.inchesToMeters(halfHeight), + Units.inchesToMeters(-halfWidth)); + Translation2d m_backLeftLocation = + new Translation2d( + Units.inchesToMeters(-halfHeight), + Units.inchesToMeters(halfWidth)); + Translation2d m_backRightLocation = + new Translation2d( + Units.inchesToMeters(-halfHeight), + Units.inchesToMeters(-halfWidth)); + + public SwerveDriveKinematics m_kinematics = new SwerveDriveKinematics(m_frontLeftLocation, m_frontRightLocation, + m_backLeftLocation, m_backRightLocation); + + public SwerveModule[] modules; + public WPI_Pigeon2 m_gyro; + + public SwerveDriveOdometry m_odometry; + // public SwerveDriveOdometry m_odometry; + + public double speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW; + public boolean ignoreAngles; + public Rotation2d rotTarget = new Rotation2d(); + private ChassisSpeeds chassisSpeeds = new ChassisSpeeds(); + + private final Field2d m_field = new Field2d(); + + public SwerveDrive(SwerveModule leftFront, SwerveModule leftBack, SwerveModule rightFront, SwerveModule rightBack, + WPI_Pigeon2 gyro) { + + m_leftFront = leftFront; + m_leftBack = leftBack; + m_rightFront = rightFront; + m_rightBack = rightBack; + m_gyro = gyro; + + modules = new SwerveModule[] {m_leftFront, m_rightFront, m_leftBack, m_rightBack}; + + // m_poseEstimator = new SwerveDrivePoseEstimator( + // getRegGyro(),//m_gyro.getRotation2d(), + // new Pose2d(), + // m_kinematics, + // VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1)), // TODO: tune + // VecBuilder.fill(Units.degreesToRadians(1)), // TODO: tune + // VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1))); // TODO: tune + + m_odometry = new SwerveDriveOdometry(m_kinematics, m_gyro.getRotation2d()); + + m_gyro.reset(); + SmartDashboard.putData("Field", m_field); + } + + public void driveWithInput(double speedX, double speedY, double rot, boolean fieldRelative) { + Translation2d speed = new Translation2d(speedX, speedY); + driveWithInput(speed, rot, fieldRelative); + } + + /** + * Method to drive the robot using joystick info. + * @link https://github.com/ZachOrr/MK3-Swerve-Example + * @param speeds[0] Speed of the robot in the x direction (forward). + * @param speeds[1] Speed of the robot in the y direction (sideways). + * @param rot Angular rate of the robot. + * @param fieldRelative Whether the provided x and y speeds are relative to the + * field. + */ + public void driveWithInput(Translation2d speed, double rot, boolean fieldRelative) { + ignoreAngles = (speed.getX() == 0) && (speed.getY() == 0) && (rot == 0); + + double mag = speed.getNorm(); + speed = speed.times(mag * speedAdjust); + + double xSpeedMetersPerSecond = speed.getX(); + double ySpeedMetersPerSecond = speed.getY(); + chassisSpeeds = fieldRelative + ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, + -rot * SwerveDriveConstants.ROTATION_SPEED * 2, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2))) + : new ChassisSpeeds(ySpeedMetersPerSecond, -xSpeedMetersPerSecond, + -rot * SwerveDriveConstants.ROTATION_SPEED * 2); + SwerveModuleState[] states = m_kinematics.toSwerveModuleStates(chassisSpeeds); + setModuleStates(states); + } + + public void driveWithInput(double leftX, double leftY, double rightX, double rightY, boolean fieldRelative) { + Translation2d speed = new Translation2d(leftX, leftY); + Translation2d head = new Translation2d(rightX, rightY); + driveWithInput(speed, head, fieldRelative); + } + + // new Rotation2d((360 - m_gyro.getRotation2d().getDegrees() + 90) * (Math.PI/180))) + public void driveWithInput(Translation2d leftStick, Translation2d rightStick, boolean fieldRelative) { + ignoreAngles = leftStick.getX() == 0 && leftStick.getY() == 0 && rightStick.getX() == 0 && rightStick.getY() == 0; + leftStick = leftStick.times(leftStick.getNorm() * speedAdjust); + if (Math.abs(rightStick.getX()) > OIConstants.RIGHT_AXIS_DEADBAND || Math.abs(rightStick.getY()) > OIConstants.RIGHT_AXIS_DEADBAND) + rotTarget = new Rotation2d(rightStick.getX(), -rightStick.getY()).minus(new Rotation2d(0,1)); + double rot = rotTarget.minus(m_gyro.getRotation2d()).getRadians(); + if (ignoreAngles) { + rot = 0; + } + double xSpeedMetersPerSecond = leftStick.getX(); + double ySpeedMetersPerSecond = leftStick.getY(); + chassisSpeeds = fieldRelative + ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, + rot * SwerveDriveConstants.ROTATION_SPEED * 2, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2))) + : new ChassisSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, rightStick.getX() * SwerveDriveConstants.ROTATION_SPEED * 2); + SwerveModuleState[] states = m_kinematics.toSwerveModuleStates( + chassisSpeeds); + // if (ignoreAngles) { + // SwerveModuleState[] lockedStates = new SwerveModuleState[states.length]; + // for (int i = 0; i < states.length; i ++) { + // SwerveModuleState state = states[i]; + // lockedStates[i]= new SwerveModuleState(0, state.angle); + // } + // setModuleStates(lockedStates); + // } + setModuleStates(states); + // SmartDashboard.putNumber("rot", rot); + // SmartDashboard.putNumber("rotarget", rotTarget.getDegrees()); + } + + /** + * Set each module of the swerve drive to the corresponding desired state. + * + * @param desiredStates Array of module states to set. + */ + public void setModuleStates(SwerveModuleState[] desiredStates) { + SwerveDriveKinematics.desaturateWheelSpeeds(desiredStates, + Units.feetToMeters(SwerveDriveConstants.MAX_SPEED_FEET_PER_SEC)); + // int i = 2; { + for (int i = 0; i < desiredStates.length; i++) { + SwerveModule module = modules[i]; + SwerveModuleState state = desiredStates[i]; + module.setDesiredState(state, ignoreAngles); + } + // modules[0].setDesiredState(desiredStates[0], false); + } + + public void setModuleRotationsToAngle(double angle) { + for (int i = 0; i < modules.length; i++) { + SwerveModule module = modules[i]; + module.rotateToAngle(angle); + } + } + + @Override + public void periodic() { + updateOdometry(); + updateSmartDash(); + + // SmartDashboard.putNumber("Pigeon getRotation2d", m_gyro.getRotation2d().getDegrees()); + // SmartDashboard.putNumber("Pigeon getAngle", m_gyro.getAngle()); + // SmartDashboard.putNumber("Pigeon Yaw", m_gyro.getYaw()); + // SmartDashboard.putNumber("Pigeon Yaw (0 to 360)", m_gyro.getYaw() % 360); + + m_field.setRobotPose(getOdometry()); + super.periodic(); + } + + private void updateSmartDash() { + // odometry + SmartDashboard.putNumber("Odometry: X", getOdometry().getX()); + SmartDashboard.putNumber("Odometry: Y", getOdometry().getY()); + SmartDashboard.putNumber("Odometry: Theta", getOdometry().getRotation().getDegrees()); + + // chassis speeds + // TODO: find the actual max velocity in m/s of the robot in fast mode to have accurate chassis speeds + // SmartDashboard.putNumber("Chassis Vel: X", chassisSpeeds.vxMetersPerSecond); + // SmartDashboard.putNumber("Chassis Vel: Y", chassisSpeeds.vyMetersPerSecond); + // SmartDashboard.putNumber("Chassis Vel: ω", chassisSpeeds.omegaRadiansPerSecond); + } + + /** + * Gets the current chassis speeds in m/s and rad/s. + * @return Current chassis speeds (vx, vy, ω) + */ + public ChassisSpeeds getChassisSpeeds() { + return chassisSpeeds; + } + + /** + * Gets the current pose of the robot. + * + * @return Robot's current pose. + */ + public Pose2d getOdometry() { + // return m_odometry.getPoseMeters(); + return m_odometry.getPoseMeters(); + // return m_poseEstimator.getEstimatedPosition(); + } + + public Pose2d getAutoOdo() { + Pose2d workingPose = getOdometry(); + return new Pose2d(-workingPose.getX(), workingPose.getY(), workingPose.getRotation()); + } + + /** + * Gets the current gyro using regression formula. + * + * @return Rotation2d object holding current gyro in radians + */ + public Rotation2d getRegGyro() { + // * test chassis regression + // double regCur = 0.6552670369 + m_gyro.getRotation2d().getDegrees() * 0.9926871527; + // * new robot regression + double regCur = 0.2507023948 + m_gyro.getRotation2d().getDegrees() * 0.999034743; + return new Rotation2d(Math.toRadians(regCur)); + } + + /** + * Resets the odometry of the robot to the given pose. + */ + public void resetOdometry(Pose2d pose) { + m_odometry.resetPosition(pose, m_gyro.getRotation2d()); + } + + /** + * Updates the field relative position of the robot. + */ + public void updateOdometry() { + Rotation2d actualDWI = new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2)); //+ (Math.PI/2)); + Rotation2d actual = new Rotation2d(m_gyro.getRotation2d().getRadians()); + + SmartDashboard.putNumber("AUTO ACTUAL GYRO", actual.getDegrees()); + SmartDashboard.putNumber("AUTO DWI GYRO", actual.getDegrees()); + + m_odometry.update( actual,//m_gyro.getRotation2d(),//new Rotation2d((2 * Math.PI) - getRegGyro().getRadians()), + modules[0].getState(), + modules[1].getState(), + modules[2].getState(), + modules[3].getState()); + } + + + /** + * Resets pigeon. + */ + public void resetGyro() { + m_gyro.reset(); + rotTarget = new Rotation2d(0); + } + + /** + * Stop all four swerve modules. + */ + public void stopModules() { + modules[0].stop(); + modules[1].stop(); + modules[2].stop(); + modules[3].stop(); + } + + /** + * Switches speed modes. + * + * @param shift True if fast mode, false if slow mode. + */ + public void highSpeed(boolean shift) { + if (shift) { + speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_FAST; + } else { + speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW; + } + } + + public double getCurrent(){ + return m_leftFront.getCurrent() + m_rightFront.getCurrent() + m_rightBack.getCurrent() + m_leftBack.getCurrent(); + } + + public double getVoltage(){ + return m_leftFront.getVoltage() + m_rightFront.getVoltage() + m_rightBack.getVoltage() + m_leftBack.getVoltage(); + } +} \ No newline at end of file diff --git a/src/main/java/frc4388/robot/subsystems/SwerveModule.java b/src/main/java/frc4388/robot/subsystems/SwerveModule.java new file mode 100644 index 0000000..68c383d --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/SwerveModule.java @@ -0,0 +1,181 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.subsystems; + +import com.ctre.phoenix.motorcontrol.FeedbackDevice; +import com.ctre.phoenix.motorcontrol.RemoteSensorSource; +import com.ctre.phoenix.motorcontrol.TalonFXControlMode; +import com.ctre.phoenix.motorcontrol.TalonFXFeedbackDevice; +import com.ctre.phoenix.motorcontrol.can.TalonFXConfiguration; +import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; +import com.ctre.phoenix.sensors.CANCoder; +import com.ctre.phoenix.sensors.CANCoderConfiguration; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc4388.robot.Constants.SwerveDriveConstants; +import frc4388.utility.Gains; + +public class SwerveModule extends SubsystemBase { + public WPI_TalonFX angleMotor; + public WPI_TalonFX driveMotor; + private CANCoder canCoder; + public static Gains m_swerveGains = SwerveDriveConstants.SWERVE_GAINS; + + private static double kEncoderTicksPerRotation = 4096; + private SwerveModuleState state; + private double canCoderFeedbackCoefficient; + + public long m_currentTime; + public long m_lastTime; + public double m_deltaTime; + + public double m_currentPos; + public double m_lastPos; + + public SwerveModuleState lastState = new SwerveModuleState(); + public SwerveModuleState currentState; + + /** Creates a new SwerveModule. */ + public SwerveModule(WPI_TalonFX driveMotor, WPI_TalonFX angleMotor, CANCoder canCoder, double offset) { + this.driveMotor = driveMotor; + this.angleMotor = angleMotor; + this.canCoder = canCoder; + canCoderFeedbackCoefficient = canCoder.configGetFeedbackCoefficient(); + + TalonFXConfiguration angleTalonFXConfiguration = new TalonFXConfiguration(); + + angleTalonFXConfiguration.slot0.kP = m_swerveGains.kP; + angleTalonFXConfiguration.slot0.kI = m_swerveGains.kI; + angleTalonFXConfiguration.slot0.kD = m_swerveGains.kD; + + // Use the CANCoder as the remote sensor for the primary TalonFX PID + angleTalonFXConfiguration.remoteFilter0.remoteSensorDeviceID = canCoder.getDeviceID(); + angleTalonFXConfiguration.remoteFilter0.remoteSensorSource = RemoteSensorSource.CANCoder; + angleTalonFXConfiguration.primaryPID.selectedFeedbackSensor = FeedbackDevice.RemoteSensor0; + angleMotor.configAllSettings(angleTalonFXConfiguration); + // angleMotor.setInverted(true); + // TalonFXConfiguration driveTalonFXConfiguration = new TalonFXConfiguration(); + // driveTalonFXConfiguration.slot0.kP = 0.05; + // driveTalonFXConfiguration.slot0.kI = 0.0; + // driveTalonFXConfiguration.slot0.kD = 0.0; + // driveTalonFXConfiguration.primaryPID.selectedFeedbackSensor = + // FeedbackDevice.IntegratedSensor; + driveMotor.configFactoryDefault(); + driveMotor.configSelectedFeedbackSensor(TalonFXFeedbackDevice.IntegratedSensor, 0, 30); + driveMotor.configNominalOutputForward(0, 30); + driveMotor.configNominalOutputReverse(0, 30); + driveMotor.configPeakOutputForward(1, 30); + driveMotor.configPeakOutputReverse(-1, 30); + driveMotor.configAllowableClosedloopError(0, 0, 30); + // driveMotor.setInverted(true); + driveMotor.config_kP(0, 0, 30); + driveMotor.config_kI(0, 0, 30); + driveMotor.config_kD(0, 0, 30); + + // driveMotor.configAllSettings(driveTalonFXConfiguration); + + CANCoderConfiguration canCoderConfiguration = new CANCoderConfiguration(); + canCoderConfiguration.sensorCoefficient = 0.087890625; + canCoderConfiguration.magnetOffsetDegrees = offset; + canCoderConfiguration.sensorDirection = true; + canCoder.configAllSettings(canCoderConfiguration); + + m_currentTime = System.currentTimeMillis(); + m_lastTime = System.currentTimeMillis(); + + m_lastPos = driveMotor.getSelectedSensorPosition(); + } + + private Rotation2d getAngle() { + // ! Note: This assumes the CANCoders are setup with the default feedback coefficient and the sensor value reports degrees. + return Rotation2d.fromDegrees(canCoder.getAbsolutePosition()); + } + + /** + * Set the speed + rotation of the swerve module from a SwerveModuleState object + * + * @param desiredState - A SwerveModuleState representing the desired new state + * of the module + */ + public void setDesiredState(SwerveModuleState desiredState, boolean ignoreAngle) { + Rotation2d currentRotation = getAngle(); + // currentRotation.getDegrees()); + state = SwerveModuleState.optimize(desiredState, currentRotation); + + // Find the difference between our current rotational position + our new + // rotational position + Rotation2d rotationDelta = state.angle.minus(currentRotation); + + // Find the new absolute position of the module based on the difference in + // rotation + double deltaTicks = (rotationDelta.getDegrees() / 360.) * kEncoderTicksPerRotation; + // Convert the CANCoder from it's position reading back to ticks + double currentTicks = canCoder.getPosition() / canCoderFeedbackCoefficient; + double desiredTicks = currentTicks + deltaTicks; + + if (!ignoreAngle) { + angleMotor.set(TalonFXControlMode.Position, desiredTicks); + } + + // Please work + double ftPerSec = Units.metersToFeet(state.speedMetersPerSecond); + double normFtPerSec = ftPerSec / SwerveDriveConstants.MAX_SPEED_FEET_PER_SEC; + // double angleCorrection = angleMotor.getSelectedSensorVelocity() * 2.69; + driveMotor.set(normFtPerSec);// - angleMotor.get()); + // driveMotor.set(TalonFXControlMode.Velocity, angleCorrection); // Ratio + // between axis = 1/1.75 Ratio of wheel is 5.14/1 ratio of steer is 12.8/1 + } + + /** + * Get current module state. + * + * @return The current state of the module in m/s. + */ + public SwerveModuleState getState() { + // return state; + return new SwerveModuleState(driveMotor.getSelectedSensorVelocity() * SwerveDriveConstants.INCHES_PER_TICK + * SwerveDriveConstants.METERS_PER_INCH * 10, getAngle()); + } + + /** + * Stop the drive and steer motors of current module. + */ + public void stop() { + driveMotor.set(0); + angleMotor.set(0); + } + + public void rotateToAngle(double angle) { + this.angleMotor.set(TalonFXControlMode.Position, angle); + } + + @Override + public void periodic() { + currentState = this.getState(); + + Rotation2d currentRotation = getAngle(); + SmartDashboard.putNumber("Angle Motor " + angleMotor.getDeviceID(), currentRotation.getDegrees()); + SmartDashboard.putNumber("Drive Motor " + driveMotor.getDeviceID(), + ((driveMotor.getSelectedSensorPosition() / 2048) * 360) % 360); + + lastState = currentState; + } + + public void reset() { + canCoder.setPositionToAbsolute(); + // canCoder.configSensorInitializationStrategy(initializationStrategy) + } + public double getCurrent(){ + return angleMotor.getSupplyCurrent() + driveMotor.getSupplyCurrent(); + } + + public double getVoltage(){ + return (Math.abs(angleMotor.getMotorOutputVoltage()) + Math.abs(driveMotor.getMotorOutputVoltage())); + } +} \ No newline at end of file From 3eeb756f372841446f66a6326c2bcb4221356fcb Mon Sep 17 00:00:00 2001 From: 66945 <54561572+66945@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:48:59 -0600 Subject: [PATCH 63/70] Update Constants.java --- src/main/java/frc4388/robot/Constants.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index 84bd57a..ee88284 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -8,6 +8,7 @@ package frc4388.robot; import frc4388.utility.LEDPatterns; +import frc4388.utility.Gains; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean From e5dafa77d2b6649cd2a8d2dac7403d977479f27b Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:56:01 -0700 Subject: [PATCH 64/70] worky --- .gitignore | 17 +- .vscode/settings.json | 18 +- .wpilib/wpilib_preferences.json | 2 +- WPILib-License.md | 24 ++ build.gradle | 94 +++-- gradle/wrapper/gradle-wrapper.jar | Bin 58702 -> 60756 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 263 +++++++----- gradlew.bat | 37 +- settings.gradle | 2 +- src/main/java/frc4388/robot/Constants.java | 136 ++++-- src/main/java/frc4388/robot/RobotMap.java | 2 +- .../frc4388/robot/subsystems/DiffDrive.java | 2 +- .../java/frc4388/robot/subsystems/LED.java | 2 +- .../frc4388/robot/subsystems/SwerveDrive.java | 372 ++++++---------- .../robot/subsystems/SwerveModule.java | 222 +++++----- src/main/java/frc4388/utility/Gains.java | 83 ++++ src/main/java/frc4388/utility/RobotGyro.java | 44 +- .../utility/controller/XboxTriggerButton.java | 3 +- .../robot/subsystems/LEDSubsystemTest.old | 59 +++ .../frc4388/utility/RobotGyroUtilityTest.old | 184 ++++++++ .../frc4388/utility/RobotTimeUtilityTest.old | 104 +++++ vendordeps/NavX.json | 39 ++ vendordeps/Phoenix.json | 397 ++++++++++++++---- vendordeps/WPILibNewCommands.json | 72 ++-- 25 files changed, 1472 insertions(+), 708 deletions(-) create mode 100644 WPILib-License.md create mode 100644 src/main/java/frc4388/utility/Gains.java create mode 100644 src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.old create mode 100644 src/test/java/frc4388/utility/RobotGyroUtilityTest.old create mode 100644 src/test/java/frc4388/utility/RobotTimeUtilityTest.old create mode 100644 vendordeps/NavX.json diff --git a/.gitignore b/.gitignore index 983678a..a8d1911 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -# Created by https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. ### C++ ### # Prerequisites @@ -151,11 +152,21 @@ gradle-app.setting # gradle/wrapper/gradle-wrapper.properties # # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project .classpath .project .settings/ bin/ -imgui.ini +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ -# End of https://www.gitignore.io/api/c++,java,linux,macos,gradle,windows,visualstudiocode +# Fleet +.fleet + +# Simulation GUI and other tools window save file +*-window.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 5200b5c..4ed293b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", "files.exclude": { "**/.git": true, "**/.svn": true, @@ -10,6 +11,19 @@ "**/.classpath": true, "**/.project": true, "**/.settings": true, - "**/.factorypath": true - } + "**/.factorypath": true, + "**/*~": true + }, + "java.test.config": [ + { + "name": "WPIlibUnitTests", + "workingDirectory": "${workspaceFolder}/build/jni/release", + "vmargs": [ "-Djava.library.path=${workspaceFolder}/build/jni/release" ], + "env": { + "LD_LIBRARY_PATH": "${workspaceFolder}/build/jni/release" , + "DYLD_LIBRARY_PATH": "${workspaceFolder}/build/jni/release" + } + }, + ], + "java.test.defaultConfig": "WPIlibUnitTests" } diff --git a/.wpilib/wpilib_preferences.json b/.wpilib/wpilib_preferences.json index a5920a4..9c62ece 100644 --- a/.wpilib/wpilib_preferences.json +++ b/.wpilib/wpilib_preferences.json @@ -1,6 +1,6 @@ { "enableCppIntellisense": false, "currentLanguage": "java", - "projectYear": "2020", + "projectYear": "2023", "teamNumber": 4388 } \ No newline at end of file diff --git a/WPILib-License.md b/WPILib-License.md new file mode 100644 index 0000000..3d5a824 --- /dev/null +++ b/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2021 FIRST and other WPILib contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of FIRST, WPILib, nor the names of other WPILib + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/build.gradle b/build.gradle index b202c6b..a2aa528 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2020.3.2" + id "edu.wpi.first.GradleRIO" version "2023.4.3" } sourceCompatibility = JavaVersion.VERSION_11 @@ -9,61 +9,91 @@ targetCompatibility = JavaVersion.VERSION_11 def ROBOT_MAIN_CLASS = "frc4388.robot.Main" // Define my targets (RoboRIO) and artifacts (deployable files) -// This is added by GradleRIO's backing project EmbeddedTools. +// This is added by GradleRIO's backing project DeployUtils. deploy { targets { - roboRIO("roborio") { + roborio(getTargetTypeClass('RoboRIO')) { // Team number is loaded either from the .wpilib/wpilib_preferences.json // or from command line. If not found an exception will be thrown. // You can use getTeamOrDefault(team) instead of getTeamNumber if you // want to store a team number in this file. - team = frc.getTeamNumber() - } - } - artifacts { - frcJavaArtifact('frcJava') { - targets << "roborio" - // Debug can be overridden by command line, for use with VSCode - debug = frc.getDebugOrDefault(false) - } - // Built in artifact to deploy arbitrary files to the roboRIO. - fileTreeArtifact('frcStaticFileDeploy') { - // The directory below is the local directory to deploy - files = fileTree(dir: 'src/main/deploy') - // Deploy to RoboRIO target, into /home/lvuser/deploy - targets << "roborio" - directory = '/home/lvuser/deploy' + team = project.frc.getTeamNumber() + debug = project.frc.getDebugOrDefault(false) + + artifacts { + // First part is artifact name, 2nd is artifact type + // getTargetTypeClass is a shortcut to get the class type using a string + + frcJava(getArtifactTypeClass('FRCJavaArtifact')) { + } + + // Static files artifact + frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { + files = project.fileTree('src/main/deploy') + directory = '/home/lvuser/deploy' + } + } } } } +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for JNI. +wpi.java.debugJni = false + // Set this to true to enable desktop support. -def includeDesktopSupport = true +def includeDesktopSupport = false // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. -// Also defines JUnit 4. +// Also defines JUnit 5. dependencies { - implementation wpi.deps.wpilib() - nativeZip wpi.deps.wpilibJni(wpi.platforms.roborio) - nativeDesktopZip wpi.deps.wpilibJni(wpi.platforms.desktop) + implementation wpi.java.deps.wpilib() + implementation wpi.java.vendor.java() + roborioDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.roborio) + roborioDebug wpi.java.vendor.jniDebug(wpi.platforms.roborio) - implementation wpi.deps.vendor.java() - nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) - nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) - testImplementation 'junit:junit:4.12' - testCompile "org.mockito:mockito-core:2.+" + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() - // Enable simulation gui support. Must check the box in vscode to enable support - // upon debugging - simulation wpi.deps.sim.gui(wpi.platforms.desktop, false) + nativeRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.desktop) + nativeRelease wpi.java.vendor.jniRelease(wpi.platforms.desktop) + simulationRelease wpi.sim.enableRelease() + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' +} + +test { + useJUnitPlatform() + systemProperty 'junit.jupiter.extensions.autodetection.enabled', 'true' } +// Simulation configuration (e.g. environment variables). +wpi.sim.addGui().defaultEnabled = true +wpi.sim.addDriverstation() + // Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar') // in order to make them all available at runtime. Also adding the manifest so WPILib // knows where to look for our Robot Class. jar { from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_MAIN_CLASS) + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +// Configure jar and deploy tasks +deployArtifact.jarTask = jar +wpi.java.configureExecutableTasks(jar) +wpi.java.configureTestTasks(test) + +// Configure string concat to always inline compile +tasks.withType(JavaCompile) { + options.compilerArgs.add '-XDstringConcat=inline' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b..249e5832f090a2944b7473328c07c9755baa3196 100644 GIT binary patch delta 25742 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 delta 23617 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d050f17..c23a1b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/gradlew b/gradlew index 2fe81a7..a69d9cb 100644 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,79 +140,101 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 9618d8d..f127cfd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +25,13 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,38 +64,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle index 81f96ab..48c039e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2020' + String frcYear = '2023' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index 0c85eb6..7077079 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -7,6 +7,8 @@ package frc4388.robot; +import edu.wpi.first.math.trajectory.TrapezoidProfile; + import frc4388.utility.LEDPatterns; import frc4388.utility.Gains; @@ -19,52 +21,96 @@ * constants are needed, to reduce verbosity. */ public final class Constants { - public static final class SwerveDriveConstants { - public static final double ROTATION_SPEED = 0.1; - public static final double WHEEL_SPEED = 0.1; - public static final double WIDTH = 22; - public static final double HEIGHT = 22; - public static final double JOYSTICK_TO_METERS_PER_SECOND = 5; - public static final double MAX_SPEED_FEET_PER_SEC = 16; - public static final double SPEED_FEET_PER_SECOND_AT_FULL_POWER = 20; - public static final int LEFT_FRONT_STEER_CAN_ID = 2; - public static final int LEFT_FRONT_WHEEL_CAN_ID = 3; - public static final int RIGHT_FRONT_STEER_CAN_ID = 4; - public static final int RIGHT_FRONT_WHEEL_CAN_ID = 5; - public static final int LEFT_BACK_STEER_CAN_ID = 6; - public static final int LEFT_BACK_WHEEL_CAN_ID = 7; - public static final int RIGHT_BACK_STEER_CAN_ID = 8; - public static final int RIGHT_BACK_WHEEL_CAN_ID = 9; - public static final int LEFT_FRONT_STEER_CAN_ENCODER_ID = 10; - public static final int RIGHT_FRONT_STEER_CAN_ENCODER_ID = 11; - public static final int LEFT_BACK_STEER_CAN_ENCODER_ID = 12; - public static final int RIGHT_BACK_STEER_CAN_ENCODER_ID = 13; - // ofsets are in degrees - //ofsets are in degrees - // public static final double LEFT_FRONT_ENCODER_OFFSET = 181.494141; - // public static final double RIGHT_FRONT_ENCODER_OFFSET = 360. - 59.238281; - // public static final double LEFT_BACK_ENCODER_OFFSET = 360. - 128.144531; - // public static final double RIGHT_BACK_ENCODER_OFFSET = 0.933594; - public static final double LEFT_FRONT_ENCODER_OFFSET = 180.0; - public static final double RIGHT_FRONT_ENCODER_OFFSET = 300.0; - public static final double LEFT_BACK_ENCODER_OFFSET = 360.0 - 128.0; - public static final double RIGHT_BACK_ENCODER_OFFSET = 0.0; - - // swerve PID constants - public static final int SWERVE_SLOT_IDX = 0; - public static final int SWERVE_PID_LOOP_IDX = 1; - public static final int SWERVE_TIMEOUT_MS = 30; - public static final Gains SWERVE_GAINS = new Gains(1.0, 0.0, 0.0, 0.0, 0, 1.0); - - // swerve configuration - public static final double NEUTRAL_DEADBAND = 0.04; - public static final double OPEN_LOOP_RAMP_RATE = 0.2; - public static final int REMOTE_0 = 0; - - // misc - public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; - } + public static final class SwerveDriveConstants { + public static final double MAX_ROT_SPEED = 1.5; + public static final double MIN_ROT_SPEED = 0.8; + public static double ROTATION_SPEED = MAX_ROT_SPEED; + public static double ROT_CORRECTION_SPEED = 10; // MIN_ROT_SPEED; + + public static final double CORRECTION_MIN = 10; + public static final double CORRECTION_MAX = 50; + + public static final double SLOW_SPEED = 0.8; + public static final double FAST_SPEED = 1.0; + public static final double TURBO_SPEED = 4.0; + + public static final class IDs { + public static final int LEFT_FRONT_WHEEL_ID = 2; + public static final int LEFT_FRONT_STEER_ID = 3; + public static final int LEFT_FRONT_ENCODER_ID = 10; + + public static final int RIGHT_FRONT_WHEEL_ID = 4; + public static final int RIGHT_FRONT_STEER_ID = 5; + public static final int RIGHT_FRONT_ENCODER_ID = 11; + + public static final int LEFT_BACK_WHEEL_ID = 6; + public static final int LEFT_BACK_STEER_ID = 7; + public static final int LEFT_BACK_ENCODER_ID = 12; + + public static final int RIGHT_BACK_WHEEL_ID = 8; + public static final int RIGHT_BACK_STEER_ID = 9; + public static final int RIGHT_BACK_ENCODER_ID = 13; + } + + public static final class PIDConstants { + public static final int SWERVE_SLOT_IDX = 0; + public static final int SWERVE_PID_LOOP_IDX = 1; + public static final Gains SWERVE_GAINS = new Gains(0.5, 0.0, 0.0, 0.0, 0, 1.0); + } + + public static final class AutoConstants { + public static final Gains X_CONTROLLER = new Gains(0.8, 0.0, 0.0); + public static final Gains Y_CONTROLLER = new Gains(0.8, 0.0, 0.0); + public static final Gains THETA_CONTROLLER = new Gains(-0.8, 0.0, 0.0); + public static final TrapezoidProfile.Constraints THETA_CONSTRAINTS = new TrapezoidProfile.Constraints(Math.PI/2, Math.PI/2); // TODO: tune + + public static final double PATH_MAX_VEL = 0.3; // TODO: find the actual value + public static final double PATH_MAX_ACC = 0.3; // TODO: find the actual value + } + + public static final class Conversions { + public static final int CANCODER_TICKS_PER_ROTATION = 4096; + + public static final double JOYSTICK_TO_METERS_PER_SECOND_FAST = 4.8; + public static final double JOYSTICK_TO_METERS_PER_SECOND_SLOW = 0.8; + + public static final double MOTOR_REV_PER_WHEEL_REV = 5.12; + public static final double MOTOR_REV_PER_STEER_REV = 12.8; + + public static final double TICKS_PER_MOTOR_REV = 2048; + public static final double WHEEL_DIAMETER_INCHES = 3.9; + public static final double INCHES_PER_WHEEL_REV = WHEEL_DIAMETER_INCHES * Math.PI; + + public static final double WHEEL_REV_PER_MOTOR_REV = 1 / MOTOR_REV_PER_WHEEL_REV; + public static final double TICKS_PER_WHEEL_REV = TICKS_PER_MOTOR_REV * MOTOR_REV_PER_WHEEL_REV; + public static final double TICKS_PER_INCH = TICKS_PER_WHEEL_REV / INCHES_PER_WHEEL_REV; + public static final double INCHES_PER_TICK = 1 / TICKS_PER_INCH; + + public static final double TICK_TIME_TO_SECONDS = 10; + public static final double SECONDS_TO_TICK_TIME = 1 / TICK_TIME_TO_SECONDS; + } + + public static final class Configurations { + public static final double OPEN_LOOP_RAMP_RATE = 0.2; // TODO: find the actual value + public static final double CLOSED_LOOP_RAMP_RATE = 0.2; // TODO: find the actual value + public static final double NEUTRAL_DEADBAND = 0.04; // TODO: find the actual value + } + + public static final double MAX_SPEED_FEET_PER_SECOND = 5; // TODO: find the actual value + public static final double MAX_ANGULAR_SPEED_FEET_PER_SECOND = 2 * 2 * Math.PI; // TODO: find the actual value + + // dimensions + public static final double WIDTH = 18.5; + public static final double HEIGHT = 18.5; + public static final double HALF_WIDTH = WIDTH / 2.d; + public static final double HALF_HEIGHT = HEIGHT / 2.d; + + // misc + public static final int TIMEOUT_MS = 30; + public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; + } + public static final class DriveConstants { public static final int DRIVE_PIGEON_ID = 6; diff --git a/src/main/java/frc4388/robot/RobotMap.java b/src/main/java/frc4388/robot/RobotMap.java index 4bec615..5f17853 100644 --- a/src/main/java/frc4388/robot/RobotMap.java +++ b/src/main/java/frc4388/robot/RobotMap.java @@ -12,7 +12,7 @@ import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; import com.ctre.phoenix.sensors.PigeonIMU; -import edu.wpi.first.wpilibj.Spark; +import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc4388.robot.Constants.LEDConstants; import frc4388.utility.RobotGyro; diff --git a/src/main/java/frc4388/robot/subsystems/DiffDrive.java b/src/main/java/frc4388/robot/subsystems/DiffDrive.java index a791913..9ec39df 100644 --- a/src/main/java/frc4388/robot/subsystems/DiffDrive.java +++ b/src/main/java/frc4388/robot/subsystems/DiffDrive.java @@ -80,6 +80,6 @@ private void updateSmartDashboard() { SmartDashboard.putBoolean("Is Gyro a Pigeon?", m_gyro.m_isGyroAPigeon); SmartDashboard.putNumber("Turn Rate", m_gyro.getRate()); SmartDashboard.putNumber("Gyro Pitch", m_gyro.getPitch()); - SmartDashboard.putData(m_gyro); + //SmartDashboard.putData(m_gyro); } } diff --git a/src/main/java/frc4388/robot/subsystems/LED.java b/src/main/java/frc4388/robot/subsystems/LED.java index 57c7b58..0d4af80 100644 --- a/src/main/java/frc4388/robot/subsystems/LED.java +++ b/src/main/java/frc4388/robot/subsystems/LED.java @@ -7,7 +7,7 @@ package frc4388.robot.subsystems; -import edu.wpi.first.wpilibj.Spark; +import edu.wpi.first.wpilibj.motorcontrol.Spark; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; diff --git a/src/main/java/frc4388/robot/subsystems/SwerveDrive.java b/src/main/java/frc4388/robot/subsystems/SwerveDrive.java index 34687d9..055230b 100644 --- a/src/main/java/frc4388/robot/subsystems/SwerveDrive.java +++ b/src/main/java/frc4388/robot/subsystems/SwerveDrive.java @@ -4,312 +4,192 @@ package frc4388.robot.subsystems; -import com.ctre.phoenix.sensors.WPI_Pigeon2; - -import edu.wpi.first.math.VecBuilder; -import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; -import edu.wpi.first.math.geometry.Pose2d; -import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.kinematics.ChassisSpeeds; import edu.wpi.first.math.kinematics.SwerveDriveKinematics; -import edu.wpi.first.math.kinematics.SwerveDriveOdometry; import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.math.util.Units; -import edu.wpi.first.wpilibj.smartdashboard.Field2d; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; -import frc4388.robot.Constants.OIConstants; import frc4388.robot.Constants.SwerveDriveConstants; -import frc4388.utility.Gains; +import frc4388.utility.RobotGyro; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; public class SwerveDrive extends SubsystemBase { + + private SwerveModule leftFront; + private SwerveModule rightFront; + private SwerveModule leftBack; + private SwerveModule rightBack; - private SwerveModule m_leftFront; - private SwerveModule m_leftBack; - private SwerveModule m_rightFront; - private SwerveModule m_rightBack; - - double halfWidth = SwerveDriveConstants.WIDTH / 2.d; - double halfHeight = SwerveDriveConstants.HEIGHT / 2.d; - - public static Gains m_swerveGains = SwerveDriveConstants.SWERVE_GAINS; - - Translation2d m_frontLeftLocation = - new Translation2d( - Units.inchesToMeters(halfHeight), - Units.inchesToMeters(halfWidth)); - Translation2d m_frontRightLocation = - new Translation2d( - Units.inchesToMeters(halfHeight), - Units.inchesToMeters(-halfWidth)); - Translation2d m_backLeftLocation = - new Translation2d( - Units.inchesToMeters(-halfHeight), - Units.inchesToMeters(halfWidth)); - Translation2d m_backRightLocation = - new Translation2d( - Units.inchesToMeters(-halfHeight), - Units.inchesToMeters(-halfWidth)); - - public SwerveDriveKinematics m_kinematics = new SwerveDriveKinematics(m_frontLeftLocation, m_frontRightLocation, - m_backLeftLocation, m_backRightLocation); - - public SwerveModule[] modules; - public WPI_Pigeon2 m_gyro; - - public SwerveDriveOdometry m_odometry; - // public SwerveDriveOdometry m_odometry; - - public double speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW; - public boolean ignoreAngles; - public Rotation2d rotTarget = new Rotation2d(); - private ChassisSpeeds chassisSpeeds = new ChassisSpeeds(); - - private final Field2d m_field = new Field2d(); + private SwerveModule[] modules; - public SwerveDrive(SwerveModule leftFront, SwerveModule leftBack, SwerveModule rightFront, SwerveModule rightBack, - WPI_Pigeon2 gyro) { + private Translation2d leftFrontLocation = new Translation2d(Units.inchesToMeters(SwerveDriveConstants.HALF_HEIGHT), Units.inchesToMeters(SwerveDriveConstants.HALF_WIDTH)); + private Translation2d rightFrontLocation = new Translation2d(Units.inchesToMeters(SwerveDriveConstants.HALF_HEIGHT), -Units.inchesToMeters(SwerveDriveConstants.HALF_WIDTH)); + private Translation2d leftBackLocation = new Translation2d(-Units.inchesToMeters(SwerveDriveConstants.HALF_HEIGHT), Units.inchesToMeters(SwerveDriveConstants.HALF_WIDTH)); + private Translation2d rightBackLocation = new Translation2d(-Units.inchesToMeters(SwerveDriveConstants.HALF_HEIGHT), -Units.inchesToMeters(SwerveDriveConstants.HALF_WIDTH)); + + private SwerveDriveKinematics kinematics = new SwerveDriveKinematics(leftFrontLocation, rightFrontLocation, leftBackLocation, rightBackLocation); - m_leftFront = leftFront; - m_leftBack = leftBack; - m_rightFront = rightFront; - m_rightBack = rightBack; - m_gyro = gyro; + private RobotGyro gyro; - modules = new SwerveModule[] {m_leftFront, m_rightFront, m_leftBack, m_rightBack}; - - // m_poseEstimator = new SwerveDrivePoseEstimator( - // getRegGyro(),//m_gyro.getRotation2d(), - // new Pose2d(), - // m_kinematics, - // VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1)), // TODO: tune - // VecBuilder.fill(Units.degreesToRadians(1)), // TODO: tune - // VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1))); // TODO: tune + public double speedAdjust = SwerveDriveConstants.Conversions.JOYSTICK_TO_METERS_PER_SECOND_SLOW; // * slow by default + + public double rotTarget = 0.0; + public ChassisSpeeds chassisSpeeds = new ChassisSpeeds(); + + /** Creates a new SwerveDrive. */ + public SwerveDrive(SwerveModule leftFront, SwerveModule rightFront, SwerveModule leftBack, SwerveModule rightBack, RobotGyro gyro) { + this.leftFront = leftFront; + this.rightFront = rightFront; + this.leftBack = leftBack; + this.rightBack = rightBack; - m_odometry = new SwerveDriveOdometry(m_kinematics, m_gyro.getRotation2d()); + this.gyro = gyro; - m_gyro.reset(); - SmartDashboard.putData("Field", m_field); + this.modules = new SwerveModule[] {this.leftFront, this.rightFront, this.leftBack, this.rightBack}; } - public void driveWithInput(double speedX, double speedY, double rot, boolean fieldRelative) { - Translation2d speed = new Translation2d(speedX, speedY); - driveWithInput(speed, rot, fieldRelative); - } + boolean stopped = false; + public void driveWithInput(Translation2d leftStick, Translation2d rightStick, boolean fieldRelative) { + if (fieldRelative) { - /** - * Method to drive the robot using joystick info. - * @link https://github.com/ZachOrr/MK3-Swerve-Example - * @param speeds[0] Speed of the robot in the x direction (forward). - * @param speeds[1] Speed of the robot in the y direction (sideways). - * @param rot Angular rate of the robot. - * @param fieldRelative Whether the provided x and y speeds are relative to the - * field. - */ - public void driveWithInput(Translation2d speed, double rot, boolean fieldRelative) { - ignoreAngles = (speed.getX() == 0) && (speed.getY() == 0) && (rot == 0); + double rot = 0; + + if (rightStick.getNorm() > 0.05) { + rotTarget = gyro.getAngle(); + rot = rightStick.getX() * SwerveDriveConstants.ROTATION_SPEED; + SmartDashboard.putBoolean("drift correction", false); + stopped = false; + } else if(leftStick.getNorm() > 0.05) { + if (!stopped) { + stopModules(); + stopped = true; + } - double mag = speed.getNorm(); - speed = speed.times(mag * speedAdjust); + SmartDashboard.putBoolean("drift correction", true); + rot = ((rotTarget - gyro.getAngle()) / 360) * SwerveDriveConstants.ROT_CORRECTION_SPEED; - double xSpeedMetersPerSecond = speed.getX(); - double ySpeedMetersPerSecond = speed.getY(); - chassisSpeeds = fieldRelative - ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, - -rot * SwerveDriveConstants.ROTATION_SPEED * 2, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2))) - : new ChassisSpeeds(ySpeedMetersPerSecond, -xSpeedMetersPerSecond, - -rot * SwerveDriveConstants.ROTATION_SPEED * 2); - SwerveModuleState[] states = m_kinematics.toSwerveModuleStates(chassisSpeeds); - setModuleStates(states); - } + } - public void driveWithInput(double leftX, double leftY, double rightX, double rightY, boolean fieldRelative) { - Translation2d speed = new Translation2d(leftX, leftY); - Translation2d head = new Translation2d(rightX, rightY); - driveWithInput(speed, head, fieldRelative); - } + // Use the left joystick to set speed. Apply a cubic curve and the set max speed. + Translation2d speed = leftStick.times(leftStick.getNorm() * speedAdjust); + // Translation2d cubedSpeed = new Translation2d(Math.pow(speed.getX(), 3.00), Math.pow(speed.getY(), 3.00)); - // new Rotation2d((360 - m_gyro.getRotation2d().getDegrees() + 90) * (Math.PI/180))) - public void driveWithInput(Translation2d leftStick, Translation2d rightStick, boolean fieldRelative) { - ignoreAngles = leftStick.getX() == 0 && leftStick.getY() == 0 && rightStick.getX() == 0 && rightStick.getY() == 0; - leftStick = leftStick.times(leftStick.getNorm() * speedAdjust); - if (Math.abs(rightStick.getX()) > OIConstants.RIGHT_AXIS_DEADBAND || Math.abs(rightStick.getY()) > OIConstants.RIGHT_AXIS_DEADBAND) - rotTarget = new Rotation2d(rightStick.getX(), -rightStick.getY()).minus(new Rotation2d(0,1)); - double rot = rotTarget.minus(m_gyro.getRotation2d()).getRadians(); - if (ignoreAngles) { - rot = 0; + // Convert field-relative speeds to robot-relative speeds. + chassisSpeeds = ChassisSpeeds.fromFieldRelativeSpeeds(-1 * speed.getX(), speed.getY(), rightStick.getX() * SwerveDriveConstants.ROTATION_SPEED, gyro.getRotation2d().times(-1)); + } else { + // Create robot-relative speeds. + chassisSpeeds = new ChassisSpeeds(-1 * leftStick.getX(), leftStick.getY(), rightStick.getX() * SwerveDriveConstants.ROTATION_SPEED); } - double xSpeedMetersPerSecond = leftStick.getX(); - double ySpeedMetersPerSecond = leftStick.getY(); - chassisSpeeds = fieldRelative - ? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, - rot * SwerveDriveConstants.ROTATION_SPEED * 2, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2))) - : new ChassisSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, rightStick.getX() * SwerveDriveConstants.ROTATION_SPEED * 2); - SwerveModuleState[] states = m_kinematics.toSwerveModuleStates( - chassisSpeeds); - // if (ignoreAngles) { - // SwerveModuleState[] lockedStates = new SwerveModuleState[states.length]; - // for (int i = 0; i < states.length; i ++) { - // SwerveModuleState state = states[i]; - // lockedStates[i]= new SwerveModuleState(0, state.angle); - // } - // setModuleStates(lockedStates); - // } - setModuleStates(states); - // SmartDashboard.putNumber("rot", rot); - // SmartDashboard.putNumber("rotarget", rotTarget.getDegrees()); + setModuleStates(kinematics.toSwerveModuleStates(chassisSpeeds)); } /** * Set each module of the swerve drive to the corresponding desired state. - * * @param desiredStates Array of module states to set. */ public void setModuleStates(SwerveModuleState[] desiredStates) { - SwerveDriveKinematics.desaturateWheelSpeeds(desiredStates, - Units.feetToMeters(SwerveDriveConstants.MAX_SPEED_FEET_PER_SEC)); - // int i = 2; { + SwerveDriveKinematics.desaturateWheelSpeeds(desiredStates, Units.feetToMeters(SwerveDriveConstants.MAX_SPEED_FEET_PER_SECOND)); for (int i = 0; i < desiredStates.length; i++) { SwerveModule module = modules[i]; SwerveModuleState state = desiredStates[i]; - module.setDesiredState(state, ignoreAngles); + module.setDesiredState(state); } - // modules[0].setDesiredState(desiredStates[0], false); } - public void setModuleRotationsToAngle(double angle) { - for (int i = 0; i < modules.length; i++) { - SwerveModule module = modules[i]; - module.rotateToAngle(angle); - } - } + public boolean rotateToTarget(double angle) { + double currentAngle = getGyroAngle(); + double error = angle - currentAngle; - @Override - public void periodic() { - updateOdometry(); - updateSmartDash(); + driveWithInput(new Translation2d(0, 0), new Translation2d(error / Math.abs(error) * 0.3, 0), true); - // SmartDashboard.putNumber("Pigeon getRotation2d", m_gyro.getRotation2d().getDegrees()); - // SmartDashboard.putNumber("Pigeon getAngle", m_gyro.getAngle()); - // SmartDashboard.putNumber("Pigeon Yaw", m_gyro.getYaw()); - // SmartDashboard.putNumber("Pigeon Yaw (0 to 360)", m_gyro.getYaw() % 360); + if (Math.abs(angle - getGyroAngle()) < 5.0) { + return true; + } - m_field.setRobotPose(getOdometry()); - super.periodic(); + return false; } - private void updateSmartDash() { - // odometry - SmartDashboard.putNumber("Odometry: X", getOdometry().getX()); - SmartDashboard.putNumber("Odometry: Y", getOdometry().getY()); - SmartDashboard.putNumber("Odometry: Theta", getOdometry().getRotation().getDegrees()); - - // chassis speeds - // TODO: find the actual max velocity in m/s of the robot in fast mode to have accurate chassis speeds - // SmartDashboard.putNumber("Chassis Vel: X", chassisSpeeds.vxMetersPerSecond); - // SmartDashboard.putNumber("Chassis Vel: Y", chassisSpeeds.vyMetersPerSecond); - // SmartDashboard.putNumber("Chassis Vel: ω", chassisSpeeds.omegaRadiansPerSecond); + public double getGyroAngle() { + return gyro.getAngle(); } - /** - * Gets the current chassis speeds in m/s and rad/s. - * @return Current chassis speeds (vx, vy, ω) - */ - public ChassisSpeeds getChassisSpeeds() { - return chassisSpeeds; + public void resetGyro() { + gyro.reset(); + rotTarget = 0.0; } - - /** - * Gets the current pose of the robot. - * - * @return Robot's current pose. - */ - public Pose2d getOdometry() { - // return m_odometry.getPoseMeters(); - return m_odometry.getPoseMeters(); - // return m_poseEstimator.getEstimatedPosition(); + + public void stopModules() { + for (SwerveModule module : this.modules) { + module.stop(); + } } - public Pose2d getAutoOdo() { - Pose2d workingPose = getOdometry(); - return new Pose2d(-workingPose.getX(), workingPose.getY(), workingPose.getRotation()); + public SwerveDriveKinematics getKinematics() { + return this.kinematics; } - /** - * Gets the current gyro using regression formula. - * - * @return Rotation2d object holding current gyro in radians - */ - public Rotation2d getRegGyro() { - // * test chassis regression - // double regCur = 0.6552670369 + m_gyro.getRotation2d().getDegrees() * 0.9926871527; - // * new robot regression - double regCur = 0.2507023948 + m_gyro.getRotation2d().getDegrees() * 0.999034743; - return new Rotation2d(Math.toRadians(regCur)); + @Override + public void periodic() { + // This method will be called once per scheduler run\ + SmartDashboard.putNumber("Gyro", getGyroAngle()); } - /** - * Resets the odometry of the robot to the given pose. - */ - public void resetOdometry(Pose2d pose) { - m_odometry.resetPosition(pose, m_gyro.getRotation2d()); + public void shiftDown() { + if (Math.abs(this.speedAdjust - SwerveDriveConstants.SLOW_SPEED) < .01) { + + } else if (Math.abs(this.speedAdjust - SwerveDriveConstants.FAST_SPEED) < .01) { + this.speedAdjust = SwerveDriveConstants.SLOW_SPEED; + } else { + this.speedAdjust = SwerveDriveConstants.FAST_SPEED; + } } - /** - * Updates the field relative position of the robot. - */ - public void updateOdometry() { - Rotation2d actualDWI = new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2)); //+ (Math.PI/2)); - Rotation2d actual = new Rotation2d(m_gyro.getRotation2d().getRadians()); - - SmartDashboard.putNumber("AUTO ACTUAL GYRO", actual.getDegrees()); - SmartDashboard.putNumber("AUTO DWI GYRO", actual.getDegrees()); - - m_odometry.update( actual,//m_gyro.getRotation2d(),//new Rotation2d((2 * Math.PI) - getRegGyro().getRadians()), - modules[0].getState(), - modules[1].getState(), - modules[2].getState(), - modules[3].getState()); + public void setToSlow() { + this.speedAdjust = SwerveDriveConstants.SLOW_SPEED; + System.out.println("SLOW"); + System.out.println("SLOW"); + System.out.println("SLOW"); + System.out.println("SLOW"); + System.out.println("SLOW"); } - - - /** - * Resets pigeon. - */ - public void resetGyro() { - m_gyro.reset(); - rotTarget = new Rotation2d(0); + + public void setToFast() { + this.speedAdjust = SwerveDriveConstants.FAST_SPEED; + System.out.println("FAST"); + System.out.println("FAST"); + System.out.println("FAST"); + System.out.println("FAST"); + System.out.println("FAST"); } - /** - * Stop all four swerve modules. - */ - public void stopModules() { - modules[0].stop(); - modules[1].stop(); - modules[2].stop(); - modules[3].stop(); + public void setToTurbo() { + this.speedAdjust = SwerveDriveConstants.TURBO_SPEED; + System.out.println("TURBO"); + System.out.println("TURBO"); + System.out.println("TURBO"); + System.out.println("TURBO"); + System.out.println("TURBO"); } - /** - * Switches speed modes. - * - * @param shift True if fast mode, false if slow mode. - */ - public void highSpeed(boolean shift) { - if (shift) { - speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_FAST; + public void shiftUp() { + if (Math.abs(this.speedAdjust - SwerveDriveConstants.SLOW_SPEED) < .01) { + this.speedAdjust = SwerveDriveConstants.FAST_SPEED; + } else if (Math.abs(this.speedAdjust - SwerveDriveConstants.FAST_SPEED) < .01) { + this.speedAdjust = SwerveDriveConstants.TURBO_SPEED; } else { - speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW; + } } - public double getCurrent(){ - return m_leftFront.getCurrent() + m_rightFront.getCurrent() + m_rightBack.getCurrent() + m_leftBack.getCurrent(); + public void toggleGear(double angle) { + if (Math.abs(this.speedAdjust - SwerveDriveConstants.Conversions.JOYSTICK_TO_METERS_PER_SECOND_SLOW) < .01 && Math.abs(angle) < 10) { + this.speedAdjust = SwerveDriveConstants.Conversions.JOYSTICK_TO_METERS_PER_SECOND_FAST; + SwerveDriveConstants.ROT_CORRECTION_SPEED = SwerveDriveConstants.CORRECTION_MIN; + } else { + this.speedAdjust = SwerveDriveConstants.Conversions.JOYSTICK_TO_METERS_PER_SECOND_SLOW; + SwerveDriveConstants.ROT_CORRECTION_SPEED = SwerveDriveConstants.CORRECTION_MIN; + } } - public double getVoltage(){ - return m_leftFront.getVoltage() + m_rightFront.getVoltage() + m_rightBack.getVoltage() + m_leftBack.getVoltage(); - } -} \ No newline at end of file +} diff --git a/src/main/java/frc4388/robot/subsystems/SwerveModule.java b/src/main/java/frc4388/robot/subsystems/SwerveModule.java index 68c383d..1ddab51 100644 --- a/src/main/java/frc4388/robot/subsystems/SwerveModule.java +++ b/src/main/java/frc4388/robot/subsystems/SwerveModule.java @@ -7,175 +7,155 @@ import com.ctre.phoenix.motorcontrol.FeedbackDevice; import com.ctre.phoenix.motorcontrol.RemoteSensorSource; import com.ctre.phoenix.motorcontrol.TalonFXControlMode; -import com.ctre.phoenix.motorcontrol.TalonFXFeedbackDevice; import com.ctre.phoenix.motorcontrol.can.TalonFXConfiguration; import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX; import com.ctre.phoenix.sensors.CANCoder; -import com.ctre.phoenix.sensors.CANCoderConfiguration; import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; import edu.wpi.first.math.kinematics.SwerveModuleState; import edu.wpi.first.math.util.Units; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc4388.robot.Constants.SwerveDriveConstants; import frc4388.utility.Gains; public class SwerveModule extends SubsystemBase { - public WPI_TalonFX angleMotor; - public WPI_TalonFX driveMotor; - private CANCoder canCoder; - public static Gains m_swerveGains = SwerveDriveConstants.SWERVE_GAINS; - - private static double kEncoderTicksPerRotation = 4096; - private SwerveModuleState state; - private double canCoderFeedbackCoefficient; - - public long m_currentTime; - public long m_lastTime; - public double m_deltaTime; - - public double m_currentPos; - public double m_lastPos; - - public SwerveModuleState lastState = new SwerveModuleState(); - public SwerveModuleState currentState; + private WPI_TalonFX driveMotor; + private WPI_TalonFX angleMotor; + private CANCoder encoder; + public static Gains swerveGains = SwerveDriveConstants.PIDConstants.SWERVE_GAINS; + /** Creates a new SwerveModule. */ - public SwerveModule(WPI_TalonFX driveMotor, WPI_TalonFX angleMotor, CANCoder canCoder, double offset) { + public SwerveModule(WPI_TalonFX driveMotor, WPI_TalonFX angleMotor, CANCoder encoder, double offset) { this.driveMotor = driveMotor; this.angleMotor = angleMotor; - this.canCoder = canCoder; - canCoderFeedbackCoefficient = canCoder.configGetFeedbackCoefficient(); - - TalonFXConfiguration angleTalonFXConfiguration = new TalonFXConfiguration(); - - angleTalonFXConfiguration.slot0.kP = m_swerveGains.kP; - angleTalonFXConfiguration.slot0.kI = m_swerveGains.kI; - angleTalonFXConfiguration.slot0.kD = m_swerveGains.kD; - - // Use the CANCoder as the remote sensor for the primary TalonFX PID - angleTalonFXConfiguration.remoteFilter0.remoteSensorDeviceID = canCoder.getDeviceID(); - angleTalonFXConfiguration.remoteFilter0.remoteSensorSource = RemoteSensorSource.CANCoder; - angleTalonFXConfiguration.primaryPID.selectedFeedbackSensor = FeedbackDevice.RemoteSensor0; - angleMotor.configAllSettings(angleTalonFXConfiguration); - // angleMotor.setInverted(true); - // TalonFXConfiguration driveTalonFXConfiguration = new TalonFXConfiguration(); - // driveTalonFXConfiguration.slot0.kP = 0.05; - // driveTalonFXConfiguration.slot0.kI = 0.0; - // driveTalonFXConfiguration.slot0.kD = 0.0; - // driveTalonFXConfiguration.primaryPID.selectedFeedbackSensor = - // FeedbackDevice.IntegratedSensor; - driveMotor.configFactoryDefault(); - driveMotor.configSelectedFeedbackSensor(TalonFXFeedbackDevice.IntegratedSensor, 0, 30); - driveMotor.configNominalOutputForward(0, 30); - driveMotor.configNominalOutputReverse(0, 30); - driveMotor.configPeakOutputForward(1, 30); - driveMotor.configPeakOutputReverse(-1, 30); - driveMotor.configAllowableClosedloopError(0, 0, 30); - // driveMotor.setInverted(true); - driveMotor.config_kP(0, 0, 30); - driveMotor.config_kI(0, 0, 30); - driveMotor.config_kD(0, 0, 30); - - // driveMotor.configAllSettings(driveTalonFXConfiguration); + this.encoder = encoder; - CANCoderConfiguration canCoderConfiguration = new CANCoderConfiguration(); - canCoderConfiguration.sensorCoefficient = 0.087890625; - canCoderConfiguration.magnetOffsetDegrees = offset; - canCoderConfiguration.sensorDirection = true; - canCoder.configAllSettings(canCoderConfiguration); + TalonFXConfiguration angleConfig = new TalonFXConfiguration(); + angleConfig.slot0.kP = swerveGains.kP; + angleConfig.slot0.kI = swerveGains.kI; + angleConfig.slot0.kD = swerveGains.kD; - m_currentTime = System.currentTimeMillis(); - m_lastTime = System.currentTimeMillis(); + // use the CANcoder as the remote sensor for the primary TalonFX PID + angleConfig.remoteFilter0.remoteSensorDeviceID = encoder.getDeviceID(); + angleConfig.remoteFilter0.remoteSensorSource = RemoteSensorSource.CANCoder; + angleConfig.primaryPID.selectedFeedbackSensor = FeedbackDevice.RemoteSensor0; + angleMotor.configAllSettings(angleConfig); - m_lastPos = driveMotor.getSelectedSensorPosition(); + encoder.configMagnetOffset(offset); + + driveMotor.setSelectedSensorPosition(0); + driveMotor.config_kP(0, 0.2); } - private Rotation2d getAngle() { - // ! Note: This assumes the CANCoders are setup with the default feedback coefficient and the sensor value reports degrees. - return Rotation2d.fromDegrees(canCoder.getAbsolutePosition()); + /** + * Get the drive motor of the SwerveModule + * @return the drive motor of the SwerveModule + */ + public WPI_TalonFX getDriveMotor() { + return this.driveMotor; } /** - * Set the speed + rotation of the swerve module from a SwerveModuleState object - * - * @param desiredState - A SwerveModuleState representing the desired new state - * of the module + * Get the angle motor of the SwerveModule + * @return the angle motor of the SwerveModule */ - public void setDesiredState(SwerveModuleState desiredState, boolean ignoreAngle) { - Rotation2d currentRotation = getAngle(); - // currentRotation.getDegrees()); - state = SwerveModuleState.optimize(desiredState, currentRotation); - - // Find the difference between our current rotational position + our new - // rotational position - Rotation2d rotationDelta = state.angle.minus(currentRotation); - - // Find the new absolute position of the module based on the difference in - // rotation - double deltaTicks = (rotationDelta.getDegrees() / 360.) * kEncoderTicksPerRotation; - // Convert the CANCoder from it's position reading back to ticks - double currentTicks = canCoder.getPosition() / canCoderFeedbackCoefficient; - double desiredTicks = currentTicks + deltaTicks; - - if (!ignoreAngle) { - angleMotor.set(TalonFXControlMode.Position, desiredTicks); - } - - // Please work - double ftPerSec = Units.metersToFeet(state.speedMetersPerSecond); - double normFtPerSec = ftPerSec / SwerveDriveConstants.MAX_SPEED_FEET_PER_SEC; - // double angleCorrection = angleMotor.getSelectedSensorVelocity() * 2.69; - driveMotor.set(normFtPerSec);// - angleMotor.get()); - // driveMotor.set(TalonFXControlMode.Velocity, angleCorrection); // Ratio - // between axis = 1/1.75 Ratio of wheel is 5.14/1 ratio of steer is 12.8/1 + public WPI_TalonFX getAngleMotor() { + return this.angleMotor; } /** - * Get current module state. - * - * @return The current state of the module in m/s. + * Get the CANcoder of the SwerveModule + * @return the CANcoder of the SwerveModule */ - public SwerveModuleState getState() { - // return state; - return new SwerveModuleState(driveMotor.getSelectedSensorVelocity() * SwerveDriveConstants.INCHES_PER_TICK - * SwerveDriveConstants.METERS_PER_INCH * 10, getAngle()); + public CANCoder getEncoder() { + return this.encoder; } /** - * Stop the drive and steer motors of current module. + * Get the angle of a SwerveModule as a Rotation2d + * @return the angle of a SwerveModule as a Rotation2d */ + public Rotation2d getAngle() { + // * Note: This assumes that the CANCoders are setup with the default feedback coefficient and the sensor value reports degrees. + return Rotation2d.fromDegrees(encoder.getAbsolutePosition()); + } + + public double getAngularVel() { + return this.angleMotor.getSelectedSensorVelocity(); + } + + public double getDrivePos() { + return this.driveMotor.getSelectedSensorPosition() / SwerveDriveConstants.Conversions.TICKS_PER_MOTOR_REV; + } + + public double getDriveVel() { + return this.driveMotor.getSelectedSensorVelocity(0); + } + public void stop() { driveMotor.set(0); angleMotor.set(0); } public void rotateToAngle(double angle) { - this.angleMotor.set(TalonFXControlMode.Position, angle); + angleMotor.set(TalonFXControlMode.Position, angle); } - @Override - public void periodic() { - currentState = this.getState(); + /** + * Get state of swerve module + * @return speed in m/s and angle in degrees + */ + public SwerveModuleState getState() { + return new SwerveModuleState( + Units.inchesToMeters(driveMotor.getSelectedSensorVelocity() * SwerveDriveConstants.Conversions.INCHES_PER_TICK) * SwerveDriveConstants.Conversions.TICK_TIME_TO_SECONDS, + getAngle() + ); + } - Rotation2d currentRotation = getAngle(); - SmartDashboard.putNumber("Angle Motor " + angleMotor.getDeviceID(), currentRotation.getDegrees()); - SmartDashboard.putNumber("Drive Motor " + driveMotor.getDeviceID(), - ((driveMotor.getSelectedSensorPosition() / 2048) * 360) % 360); + /** + * Returns the current position of the SwerveModule + * @return The current position of the SwerveModule in meters traveled by the driveMotor and the angle of the angleMotor. + */ + public SwerveModulePosition getPosition() { + return new SwerveModulePosition(Units.inchesToMeters(driveMotor.getSelectedSensorPosition() * SwerveDriveConstants.Conversions.INCHES_PER_TICK), getAngle()); + } - lastState = currentState; + /** + * Set the speed and rotation of the SwerveModule from a SwerveModuleState object + * @param desiredState a SwerveModuleState representing the desired new state of the module + */ + public void setDesiredState(SwerveModuleState desiredState) { + Rotation2d currentRotation = this.getAngle(); + + SwerveModuleState state = SwerveModuleState.optimize(desiredState, currentRotation); + + // calculate the difference between our current rotational position and our new rotational position + Rotation2d rotationDelta = state.angle.minus(currentRotation); + + // calculate the new absolute position of the SwerveModule based on the difference in rotation + double deltaTicks = (rotationDelta.getDegrees() / 360.) * SwerveDriveConstants.Conversions.CANCODER_TICKS_PER_ROTATION; + + // convert the CANCoder from its position reading to ticks + double currentTicks = encoder.getPosition() / encoder.configGetFeedbackCoefficient(); + + angleMotor.set(TalonFXControlMode.Position, currentTicks + deltaTicks); + + double feetPerSecond = Units.metersToFeet(state.speedMetersPerSecond); + + driveMotor.set((feetPerSecond / SwerveDriveConstants.MAX_SPEED_FEET_PER_SECOND)); } - public void reset() { - canCoder.setPositionToAbsolute(); - // canCoder.configSensorInitializationStrategy(initializationStrategy) + public void reset(double position) { + encoder.setPositionToAbsolute(); } - public double getCurrent(){ + + public double getCurrent() { return angleMotor.getSupplyCurrent() + driveMotor.getSupplyCurrent(); } - public double getVoltage(){ + public double getVoltage() { return (Math.abs(angleMotor.getMotorOutputVoltage()) + Math.abs(driveMotor.getMotorOutputVoltage())); } -} \ No newline at end of file +} diff --git a/src/main/java/frc4388/utility/Gains.java b/src/main/java/frc4388/utility/Gains.java new file mode 100644 index 0000000..7a3a026 --- /dev/null +++ b/src/main/java/frc4388/utility/Gains.java @@ -0,0 +1,83 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.utility; + +/** Add your docs here. */ +public class Gains { + public double kP; + public double kI; + public double kD; + public double kF; + public int kIZone; + public double kPeakOutput; + public double kMaxOutput; + public double kMinOutput; + + /** + * Creates Gains object for PIDs + * @param kP The P value. + * @param kI The I value. + * @param kD The D value. + * @param kF The F value. + * @param kIZone The zone of the I value. + * @param kPeakOutput The peak output setting the motors to run the gains at, in both forward and reverse directions. By default 1.0. + */ + public Gains(double kP, double kI, double kD, double kF, int kIZone, double kPeakOutput) { + this.kP = kP; + this.kI = kI; + this.kD = kD; + this.kF = kF; + this.kIZone = kIZone; + this.kPeakOutput = kPeakOutput; + this.kMaxOutput = kPeakOutput; + this.kMinOutput = -kPeakOutput; + } + + /** + * Creates Gains object for PIDs + * @param kP The P value. + * @param kI The I value. + * @param kD The D value. + * @param kF The F value. + * @param kIZone The zone of the I value. + */ + public Gains(double kP, double kI, double kD, double kF, int kIZone) { + this.kP = kP; + this.kI = kI; + this.kD = kD; + this.kF = kF; + this.kIZone = kIZone; + this.kPeakOutput = 1.0; + this.kMaxOutput = 1.0; + this.kMinOutput = -1.0; + } + + public Gains(double kP, double kI, double kD) { + this.kP = kP; + this.kI = kI; + this.kD = kD; + } + + /** + * Creates Gains object for PIDs + * @param kP The P value. + * @param kI The I value. + * @param kD The D value. + * @param kF The F value. + * @param kIZone The zone of the I value. + * @param kMinOutput The lowest output setting to run the gains at, usually in the reverse direction. By default -1.0. + * @param kMaxOutput The highest output setting to run the gains at, usually in the forward direction. By default 1.0. + */ + public Gains(double kP, double kI, double kD, double kF, int kIZone, double kMaxOutput, double kMinOutput) { + this.kP = kP; + this.kI = kI; + this.kD = kD; + this.kF = kF; + this.kIZone = kIZone; + this.kMaxOutput = kMaxOutput; + this.kMinOutput = kMinOutput; + this.kPeakOutput = (Math.abs(kMinOutput) > Math.abs(kMaxOutput)) ? Math.abs(kMinOutput) : Math.abs(kMaxOutput); + } +} \ No newline at end of file diff --git a/src/main/java/frc4388/utility/RobotGyro.java b/src/main/java/frc4388/utility/RobotGyro.java index a42e8c8..a037914 100644 --- a/src/main/java/frc4388/utility/RobotGyro.java +++ b/src/main/java/frc4388/utility/RobotGyro.java @@ -7,31 +7,34 @@ package frc4388.utility; -import com.ctre.phoenix.sensors.PigeonIMU; -import com.ctre.phoenix.sensors.PigeonIMU.CalibrationMode; +import com.ctre.phoenix.sensors.WPI_Pigeon2; import com.kauailabs.navx.frc.AHRS; -import edu.wpi.first.wpilibj.GyroBase; -import edu.wpi.first.wpiutil.math.MathUtil; +// import edu.wpi.first.wpilibj.GyroBase; +import edu.wpi.first.wpilibj.interfaces.Gyro; +import edu.wpi.first.math.MathUtil; /** * Gyro class that allows for interchangeable use between a pigeon and a navX */ -public class RobotGyro extends GyroBase { +public class RobotGyro implements Gyro { private RobotTime m_robotTime = RobotTime.getInstance(); - private PigeonIMU m_pigeon = null; + private WPI_Pigeon2 m_pigeon = null; private AHRS m_navX = null; public boolean m_isGyroAPigeon; //true if pigeon, false if navX private double m_lastPigeonAngle; private double m_deltaPigeonAngle; + private double pitchZero = 0; + private double rollZero = 0; + /** * Creates a Gyro based on a pigeon * @param gyro the gyroscope to use for Gyro */ - public RobotGyro(PigeonIMU gyro) { + public RobotGyro(WPI_Pigeon2 gyro) { m_pigeon = gyro; m_isGyroAPigeon = true; } @@ -45,6 +48,16 @@ public RobotGyro(AHRS gyro){ m_isGyroAPigeon = false; } + /** + * Resets yaw, pitch, and roll. + */ + public void resetZeroValues() { + if (!m_isGyroAPigeon) return; + + pitchZero = m_pigeon.getPitch(); + rollZero = m_pigeon.getRoll(); + } + /** * Run in periodic if you are using a pigeon. Updates a delta angle so that it can calculate getRate(). Note * that the getRate() method for a navX will likely be much more accurate than for a pigeon. @@ -74,7 +87,7 @@ public void updatePigeonDeltas() { @Override public void calibrate() { if (m_isGyroAPigeon) { - m_pigeon.enterCalibrationMode(CalibrationMode.Temperature); + m_pigeon.calibrate(); } else { m_navX.calibrate(); } @@ -82,6 +95,8 @@ public void calibrate() { @Override public void reset() { + resetZeroValues(); + if (m_isGyroAPigeon) { m_pigeon.setYaw(0); } else { @@ -98,9 +113,10 @@ public void reset() { * Roll is within [-90,+90] degrees. */ private double[] getPigeonAngles() { - double[] angles = new double[3]; - m_pigeon.getYawPitchRoll(angles); - return angles; + double[] ypr = new double[3]; + m_pigeon.getYawPitchRoll(ypr); + + return new double[] {ypr[0], (ypr[1] - pitchZero), (ypr[2] - rollZero)}; } @Override @@ -112,6 +128,10 @@ public double getAngle() { } } + public double getYaw() { + return this.getAngle(); + } + /** * Gets an absolute heading of the robot * @return heading from -180 to 180 degrees @@ -165,7 +185,7 @@ public double getRate() { } } - public PigeonIMU getPigeon(){ + public WPI_Pigeon2 getPigeon(){ return m_pigeon; } diff --git a/src/main/java/frc4388/utility/controller/XboxTriggerButton.java b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java index 26372c2..8c2fe88 100644 --- a/src/main/java/frc4388/utility/controller/XboxTriggerButton.java +++ b/src/main/java/frc4388/utility/controller/XboxTriggerButton.java @@ -1,7 +1,6 @@ package frc4388.utility.controller; import edu.wpi.first.wpilibj2.command.button.Button; -import frc4388.utility.controller.XboxController; /** * Mapping for the Xbox controller triggers to allow triggers to be defined as @@ -32,7 +31,7 @@ public XboxTriggerButton(XboxController controller, int trigger) { } /** {@inheritDoc} */ - @Override + // @Override public boolean get() { if (m_trigger == RIGHT_TRIGGER) { return m_controller.getRightTrigger(); diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.old b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.old new file mode 100644 index 0000000..8fcbf53 --- /dev/null +++ b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.old @@ -0,0 +1,59 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.robot.subsystems; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import edu.wpi.first.wpilibj.*; +import frc4388.robot.Constants.LEDConstants; +import frc4388.utility.LEDPatterns; + +/** + * Add your docs here. + */ +public class LEDSubsystemTest { + @Test + public void testConstructor() { + // Arrange + Spark ledController = mock(Spark.class); + + // Act + LED led = new LED(ledController); + + // Assert + assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); + } + + @Test + public void testPatterns() { + // Arrange + Spark ledController = mock(Spark.class); + LED led = new LED(ledController); + + // Act + led.setPattern(LEDPatterns.RAINBOW_RAINBOW); + + // Assert + assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); + + // Act + led.setPattern(LEDPatterns.BLUE_BREATH); + + // Assert + assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); + + // Act + led.setPattern(LEDPatterns.SOLID_BLACK); + + // Assert + assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); + } +} diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.old b/src/test/java/frc4388/utility/RobotGyroUtilityTest.old new file mode 100644 index 0000000..8c0b1e5 --- /dev/null +++ b/src/test/java/frc4388/utility/RobotGyroUtilityTest.old @@ -0,0 +1,184 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.kauailabs.navx.frc.AHRS; + +import org.junit.*; + +import frc4388.mocks.MockPigeonIMU; +import frc4388.robot.Constants.DriveConstants; + +/** + * Add your docs here. + */ +public class RobotGyroUtilityTest { + // TODO UNTESTED: most functions for NavX + + private RobotGyro gyroPigeon; + private RobotGyro gyroNavX; + + @Test + public void testConstructor() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + AHRS navX = mock(AHRS.class); + gyroPigeon = new RobotGyro(pigeon); + gyroNavX = new RobotGyro(navX); + + // Assert + assertEquals(true, gyroPigeon.m_isGyroAPigeon); + assertEquals(pigeon, gyroPigeon.getPigeon()); + assertEquals(null, gyroPigeon.getNavX()); + assertEquals(false, gyroNavX.m_isGyroAPigeon); + assertEquals(navX, gyroNavX.getNavX()); + assertEquals(null, gyroNavX.getPigeon()); + } + + @Test + public void testHeadingPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + gyroPigeon = new RobotGyro(pigeon); + + // Act & Assert + assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); + assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); + assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); + assertEquals(30, gyroPigeon.getHeading(30), 0.0001); + assertEquals(0, gyroPigeon.getHeading(0), 0.0001); + assertEquals(180, gyroPigeon.getHeading(180), 0.0001); + assertEquals(-180, gyroPigeon.getHeading(-180), 0.0001); + assertEquals(97, gyroPigeon.getHeading(1537), 0.0001); + assertEquals(99, gyroPigeon.getHeading(-2781), 0.0001); + } + + @Test + public void testYawPitchRollPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + gyroPigeon = new RobotGyro(pigeon); + + // Assert + assertEquals(0, gyroPigeon.getAngle(), 0.0001); + + // Act + pigeon.setYaw(40); + + // Assert + assertEquals(40, gyroPigeon.getAngle(), 0.0001); + + // Act + gyroPigeon.reset(); + + // Assert + assertEquals(0, gyroPigeon.getAngle(), 0.0001); + + // Act + pigeon.setYaw(-1457); + pigeon.setCurrentPitch(100); + pigeon.setCurrentRoll(100); + + // Assert + assertEquals(-1457, gyroPigeon.getAngle(), 0.0001); + assertEquals(90, gyroPigeon.getPitch(), 0.0001); + assertEquals(90, gyroPigeon.getRoll(), 0.0001); + + // Act + pigeon.setCurrentPitch(45); + pigeon.setCurrentRoll(45); + + // Assert + assertEquals(45, gyroPigeon.getPitch(), 0.0001); + assertEquals(45, gyroPigeon.getRoll(), 0.0001); + + // Act + pigeon.setCurrentPitch(0); + pigeon.setCurrentRoll(0); + + // Assert + assertEquals(0, gyroPigeon.getPitch(), 0.0001); + assertEquals(0, gyroPigeon.getRoll(), 0.0001); + + // Act + pigeon.setCurrentPitch(-60); + pigeon.setCurrentRoll(-60); + + // Assert + assertEquals(-60, gyroPigeon.getPitch(), 0.0001); + assertEquals(-60, gyroPigeon.getRoll(), 0.0001); + + // Act + pigeon.setCurrentPitch(-90); + pigeon.setCurrentRoll(-90); + + // Assert + assertEquals(-90, gyroPigeon.getPitch(), 0.0001); + assertEquals(-90, gyroPigeon.getRoll(), 0.0001); + + // Act + pigeon.setCurrentPitch(-100); + pigeon.setCurrentRoll(-100); + + // Assert + assertEquals(-90, gyroPigeon.getPitch(), 0.0001); + assertEquals(-90, gyroPigeon.getRoll(), 0.0001); + } + + @Test + public void testRatesPigeon() { + // Arrange + MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); + gyroPigeon = new RobotGyro(pigeon); + RobotTime robotTime = RobotTime.getInstance(); + gyroPigeon.updatePigeonDeltas(); + + // Act + robotTime.m_deltaTime = 5; + pigeon.setYaw(0); + gyroPigeon.updatePigeonDeltas(); + + // Assert + assertEquals(0, gyroPigeon.getRate(), 1); + + // Act + robotTime.m_deltaTime = 5; + pigeon.setYaw(90); + gyroPigeon.updatePigeonDeltas(); + + // Assert + assertEquals(18000, gyroPigeon.getRate(), 0.001); + + // Act + robotTime.m_deltaTime = 5; + pigeon.setYaw(90); + gyroPigeon.updatePigeonDeltas(); + + // Assert + assertEquals(0, gyroPigeon.getRate(), 0.001); + + // Act + robotTime.m_deltaTime = 3; + pigeon.setYaw(-30); + gyroPigeon.updatePigeonDeltas(); + + // Assert + assertEquals(-40000, gyroPigeon.getRate(), 0.001); + + // Act + robotTime.m_deltaTime = 6; + pigeon.setYaw(690); + gyroPigeon.updatePigeonDeltas(); + + // Assert + assertEquals(120000, gyroPigeon.getRate(), 0.001); + } +} diff --git a/src/test/java/frc4388/utility/RobotTimeUtilityTest.old b/src/test/java/frc4388/utility/RobotTimeUtilityTest.old new file mode 100644 index 0000000..2c31a34 --- /dev/null +++ b/src/test/java/frc4388/utility/RobotTimeUtilityTest.old @@ -0,0 +1,104 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package frc4388.utility; + +import static org.junit.Assert.*; + +import org.junit.*; + +/** + * Add your docs here. + */ +public class RobotTimeUtilityTest { + RobotTime robotTime = RobotTime.getInstance(); + + @Test + public void testUpdateTimes() { + // Arrange + long lastTime; + robotTime.m_deltaTime = 0; + robotTime.m_robotTime = 0; + robotTime.m_lastRobotTime = 0; + robotTime.m_frameNumber = 0; + robotTime.endMatchTime(); + robotTime.m_lastMatchTime = 0; + + // Assert + assertEquals(0, robotTime.m_deltaTime); + assertEquals(0, robotTime.m_robotTime); + assertEquals(0, robotTime.m_lastRobotTime); + assertEquals(0, robotTime.m_frameNumber); + lastTime = robotTime.m_robotTime; + + // Act + wait(1); + robotTime.updateTimes(); + + // Assert + assertEquals(true, robotTime.m_deltaTime > 0); + assertEquals(true, robotTime.m_robotTime > 0); + assertEquals(lastTime, robotTime.m_lastRobotTime); + assertEquals(1, robotTime.m_frameNumber); + lastTime = robotTime.m_robotTime; + + // Act + wait(1); + robotTime.updateTimes(); + + // Assert + assertEquals(true, robotTime.m_deltaTime > 0); + assertEquals(true, robotTime.m_robotTime > 0); + assertEquals(lastTime, robotTime.m_lastRobotTime); + assertEquals(2, robotTime.m_frameNumber); + } + + @Test + public void testMatchTime() { + // Arrange + long lastTime; + + // Assert + assertEquals(0, robotTime.m_matchTime); + assertEquals(0, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + // Act + robotTime.startMatchTime(); + wait(1); + robotTime.updateTimes(); + + // Assert + assertEquals(true, robotTime.m_matchTime > 0); + assertEquals(lastTime, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + // Act + wait(1); + robotTime.updateTimes(); + robotTime.endMatchTime(); + + // Assert + assertEquals(0, robotTime.m_matchTime); + assertEquals(lastTime, robotTime.m_lastMatchTime); + lastTime = robotTime.m_matchTime; + + // Act + wait(1); + robotTime.updateTimes(); + + // Assert + assertEquals(0, robotTime.m_matchTime); + assertEquals(lastTime, robotTime.m_lastMatchTime); + } + + private void wait(int millis) { + try { + Thread.sleep(millis); + } catch (Exception e) {} + } +} diff --git a/vendordeps/NavX.json b/vendordeps/NavX.json new file mode 100644 index 0000000..33d81f6 --- /dev/null +++ b/vendordeps/NavX.json @@ -0,0 +1,39 @@ +{ + "fileName": "NavX.json", + "name": "KauaiLabs_navX_FRC", + "version": "2023.0.4", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "mavenUrls": [ + "https://dev.studica.com/maven/release/2023/" + ], + "jsonUrl": "https://dev.studica.com/releases/2023/NavX.json", + "javaDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-frc-java", + "version": "2023.0.4" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-frc-cpp", + "version": "2023.0.4", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": false, + "libName": "navx_frc", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxraspbian", + "linuxarm32", + "linuxarm64", + "linuxx86-64", + "osxuniversal", + "windowsx86-64" + ] + } + ] +} \ No newline at end of file diff --git a/vendordeps/Phoenix.json b/vendordeps/Phoenix.json index f8d42a4..614dc3a 100644 --- a/vendordeps/Phoenix.json +++ b/vendordeps/Phoenix.json @@ -1,180 +1,423 @@ { "fileName": "Phoenix.json", - "name": "CTRE-Phoenix", - "version": "5.17.3", + "name": "CTRE-Phoenix (v5)", + "version": "5.31.0+23.2.2", + "frcYear": 2023, "uuid": "ab676553-b602-441f-a38d-f1296eff6537", "mavenUrls": [ - "http://devsite.ctr-electronics.com/maven/release/" + "https://maven.ctr-electronics.com/release/" ], - "jsonUrl": "http://devsite.ctr-electronics.com/maven/release/com/ctre/phoenix/Phoenix-latest.json", + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2023-latest.json", "javaDependencies": [ { "groupId": "com.ctre.phoenix", "artifactId": "api-java", - "version": "5.17.3" + "version": "5.31.0" }, { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-java", - "version": "5.17.3" + "version": "5.31.0" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.17.3", + "version": "5.31.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "diagnostics", - "version": "5.17.3", + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.31.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "canutils", - "version": "5.17.3", + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "23.2.2", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "platform-stub", - "version": "5.17.3", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "23.2.2", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "core", - "version": "5.17.3", + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "23.2.2", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "23.2.2", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" } ], "cppDependencies": [ { "groupId": "com.ctre.phoenix", "artifactId": "wpiapi-cpp", - "version": "5.17.3", + "version": "5.31.0", "libName": "CTRE_Phoenix_WPI", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { "groupId": "com.ctre.phoenix", "artifactId": "api-cpp", - "version": "5.17.3", + "version": "5.31.0", "libName": "CTRE_Phoenix", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { "groupId": "com.ctre.phoenix", "artifactId": "cci", - "version": "5.17.3", + "version": "5.31.0", "libName": "CTRE_PhoenixCCI", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "diagnostics", - "version": "5.17.3", - "libName": "CTRE_PhoenixDiagnostics", + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "23.2.2", + "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "canutils", - "version": "5.17.3", - "libName": "CTRE_PhoenixCanutils", + "groupId": "com.ctre.phoenix.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "5.31.0", + "libName": "CTRE_Phoenix_WPISim", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "platform-stub", - "version": "5.17.3", - "libName": "CTRE_PhoenixPlatform", + "groupId": "com.ctre.phoenix.sim", + "artifactId": "api-cpp-sim", + "version": "5.31.0", + "libName": "CTRE_PhoenixSim", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" }, { - "groupId": "com.ctre.phoenix", - "artifactId": "core", - "version": "5.17.3", - "libName": "CTRE_PhoenixCore", + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.31.0", + "libName": "CTRE_PhoenixCCISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "23.2.2", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "23.2.2", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "23.2.2", + "libName": "CTRE_SimTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "23.2.2", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "23.2.2", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "23.2.2", + "libName": "CTRE_SimCANCoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "23.2.2", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "23.2.2", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "23.2.2", + "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", - "sharedLibrary": false, + "sharedLibrary": true, "skipInvalidPlatforms": true, "binaryPlatforms": [ - "linuxathena", "windowsx86-64", - "linuxx86-64" - ] + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" } ] } \ No newline at end of file diff --git a/vendordeps/WPILibNewCommands.json b/vendordeps/WPILibNewCommands.json index 83de291..bd535bf 100644 --- a/vendordeps/WPILibNewCommands.json +++ b/vendordeps/WPILibNewCommands.json @@ -1,37 +1,37 @@ { - "fileName": "WPILibNewCommands.json", - "name": "WPILib-New-Commands", - "version": "2020.0.0", - "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", - "mavenUrls": [], - "jsonUrl": "", - "javaDependencies": [ - { - "groupId": "edu.wpi.first.wpilibNewCommands", - "artifactId": "wpilibNewCommands-java", - "version": "wpilib" - } - ], - "jniDependencies": [], - "cppDependencies": [ - { - "groupId": "edu.wpi.first.wpilibNewCommands", - "artifactId": "wpilibNewCommands-cpp", - "version": "wpilib", - "libName": "wpilibNewCommands", - "headerClassifier": "headers", - "sourcesClassifier": "sources", - "sharedLibrary": true, - "skipInvalidPlatforms": true, - "binaryPlatforms": [ - "linuxathena", - "linuxraspbian", - "linuxaarch64bionic", - "windowsx86-64", - "windowsx86", - "linuxx86-64", - "osxx86-64" - ] - } - ] -} \ No newline at end of file + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-cpp", + "version": "wpilib", + "libName": "wpilibNewCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} From 0c313a3d1e563aa4e65635e33ef32f5763bf4a01 Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:58:24 -0700 Subject: [PATCH 65/70] Worky 2 --- .../robot/subsystems/LEDSubsystemTest.java | 59 ------ .../frc4388/utility/RobotGyroUtilityTest.java | 184 ------------------ .../frc4388/utility/RobotTimeUtilityTest.java | 104 ---------- 3 files changed, 347 deletions(-) delete mode 100644 src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java delete mode 100644 src/test/java/frc4388/utility/RobotGyroUtilityTest.java delete mode 100644 src/test/java/frc4388/utility/RobotTimeUtilityTest.java diff --git a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java b/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java deleted file mode 100644 index 8fcbf53..0000000 --- a/src/test/java/frc4388/robot/subsystems/LEDSubsystemTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.robot.subsystems; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; - -import org.junit.Test; - -import edu.wpi.first.wpilibj.*; -import frc4388.robot.Constants.LEDConstants; -import frc4388.utility.LEDPatterns; - -/** - * Add your docs here. - */ -public class LEDSubsystemTest { - @Test - public void testConstructor() { - // Arrange - Spark ledController = mock(Spark.class); - - // Act - LED led = new LED(ledController); - - // Assert - assertEquals(LEDConstants.DEFAULT_PATTERN.getValue(), led.getPattern().getValue(), 0.0001); - } - - @Test - public void testPatterns() { - // Arrange - Spark ledController = mock(Spark.class); - LED led = new LED(ledController); - - // Act - led.setPattern(LEDPatterns.RAINBOW_RAINBOW); - - // Assert - assertEquals(LEDPatterns.RAINBOW_RAINBOW.getValue(), led.getPattern().getValue(), 0.0001); - - // Act - led.setPattern(LEDPatterns.BLUE_BREATH); - - // Assert - assertEquals(LEDPatterns.BLUE_BREATH.getValue(), led.getPattern().getValue(), 0.0001); - - // Act - led.setPattern(LEDPatterns.SOLID_BLACK); - - // Assert - assertEquals(LEDPatterns.SOLID_BLACK.getValue(), led.getPattern().getValue(), 0.0001); - } -} diff --git a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java b/src/test/java/frc4388/utility/RobotGyroUtilityTest.java deleted file mode 100644 index 8c0b1e5..0000000 --- a/src/test/java/frc4388/utility/RobotGyroUtilityTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.utility; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -import com.kauailabs.navx.frc.AHRS; - -import org.junit.*; - -import frc4388.mocks.MockPigeonIMU; -import frc4388.robot.Constants.DriveConstants; - -/** - * Add your docs here. - */ -public class RobotGyroUtilityTest { - // TODO UNTESTED: most functions for NavX - - private RobotGyro gyroPigeon; - private RobotGyro gyroNavX; - - @Test - public void testConstructor() { - // Arrange - MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - AHRS navX = mock(AHRS.class); - gyroPigeon = new RobotGyro(pigeon); - gyroNavX = new RobotGyro(navX); - - // Assert - assertEquals(true, gyroPigeon.m_isGyroAPigeon); - assertEquals(pigeon, gyroPigeon.getPigeon()); - assertEquals(null, gyroPigeon.getNavX()); - assertEquals(false, gyroNavX.m_isGyroAPigeon); - assertEquals(navX, gyroNavX.getNavX()); - assertEquals(null, gyroNavX.getPigeon()); - } - - @Test - public void testHeadingPigeon() { - // Arrange - MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - gyroPigeon = new RobotGyro(pigeon); - - // Act & Assert - assertEquals(-90, gyroPigeon.getHeading(270), 0.0001); - assertEquals(-45, gyroPigeon.getHeading(315), 0.0001); - assertEquals(-60, gyroPigeon.getHeading(-60), 0.0001); - assertEquals(30, gyroPigeon.getHeading(30), 0.0001); - assertEquals(0, gyroPigeon.getHeading(0), 0.0001); - assertEquals(180, gyroPigeon.getHeading(180), 0.0001); - assertEquals(-180, gyroPigeon.getHeading(-180), 0.0001); - assertEquals(97, gyroPigeon.getHeading(1537), 0.0001); - assertEquals(99, gyroPigeon.getHeading(-2781), 0.0001); - } - - @Test - public void testYawPitchRollPigeon() { - // Arrange - MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - gyroPigeon = new RobotGyro(pigeon); - - // Assert - assertEquals(0, gyroPigeon.getAngle(), 0.0001); - - // Act - pigeon.setYaw(40); - - // Assert - assertEquals(40, gyroPigeon.getAngle(), 0.0001); - - // Act - gyroPigeon.reset(); - - // Assert - assertEquals(0, gyroPigeon.getAngle(), 0.0001); - - // Act - pigeon.setYaw(-1457); - pigeon.setCurrentPitch(100); - pigeon.setCurrentRoll(100); - - // Assert - assertEquals(-1457, gyroPigeon.getAngle(), 0.0001); - assertEquals(90, gyroPigeon.getPitch(), 0.0001); - assertEquals(90, gyroPigeon.getRoll(), 0.0001); - - // Act - pigeon.setCurrentPitch(45); - pigeon.setCurrentRoll(45); - - // Assert - assertEquals(45, gyroPigeon.getPitch(), 0.0001); - assertEquals(45, gyroPigeon.getRoll(), 0.0001); - - // Act - pigeon.setCurrentPitch(0); - pigeon.setCurrentRoll(0); - - // Assert - assertEquals(0, gyroPigeon.getPitch(), 0.0001); - assertEquals(0, gyroPigeon.getRoll(), 0.0001); - - // Act - pigeon.setCurrentPitch(-60); - pigeon.setCurrentRoll(-60); - - // Assert - assertEquals(-60, gyroPigeon.getPitch(), 0.0001); - assertEquals(-60, gyroPigeon.getRoll(), 0.0001); - - // Act - pigeon.setCurrentPitch(-90); - pigeon.setCurrentRoll(-90); - - // Assert - assertEquals(-90, gyroPigeon.getPitch(), 0.0001); - assertEquals(-90, gyroPigeon.getRoll(), 0.0001); - - // Act - pigeon.setCurrentPitch(-100); - pigeon.setCurrentRoll(-100); - - // Assert - assertEquals(-90, gyroPigeon.getPitch(), 0.0001); - assertEquals(-90, gyroPigeon.getRoll(), 0.0001); - } - - @Test - public void testRatesPigeon() { - // Arrange - MockPigeonIMU pigeon = new MockPigeonIMU(DriveConstants.DRIVE_PIGEON_ID); - gyroPigeon = new RobotGyro(pigeon); - RobotTime robotTime = RobotTime.getInstance(); - gyroPigeon.updatePigeonDeltas(); - - // Act - robotTime.m_deltaTime = 5; - pigeon.setYaw(0); - gyroPigeon.updatePigeonDeltas(); - - // Assert - assertEquals(0, gyroPigeon.getRate(), 1); - - // Act - robotTime.m_deltaTime = 5; - pigeon.setYaw(90); - gyroPigeon.updatePigeonDeltas(); - - // Assert - assertEquals(18000, gyroPigeon.getRate(), 0.001); - - // Act - robotTime.m_deltaTime = 5; - pigeon.setYaw(90); - gyroPigeon.updatePigeonDeltas(); - - // Assert - assertEquals(0, gyroPigeon.getRate(), 0.001); - - // Act - robotTime.m_deltaTime = 3; - pigeon.setYaw(-30); - gyroPigeon.updatePigeonDeltas(); - - // Assert - assertEquals(-40000, gyroPigeon.getRate(), 0.001); - - // Act - robotTime.m_deltaTime = 6; - pigeon.setYaw(690); - gyroPigeon.updatePigeonDeltas(); - - // Assert - assertEquals(120000, gyroPigeon.getRate(), 0.001); - } -} diff --git a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java b/src/test/java/frc4388/utility/RobotTimeUtilityTest.java deleted file mode 100644 index 2c31a34..0000000 --- a/src/test/java/frc4388/utility/RobotTimeUtilityTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package frc4388.utility; - -import static org.junit.Assert.*; - -import org.junit.*; - -/** - * Add your docs here. - */ -public class RobotTimeUtilityTest { - RobotTime robotTime = RobotTime.getInstance(); - - @Test - public void testUpdateTimes() { - // Arrange - long lastTime; - robotTime.m_deltaTime = 0; - robotTime.m_robotTime = 0; - robotTime.m_lastRobotTime = 0; - robotTime.m_frameNumber = 0; - robotTime.endMatchTime(); - robotTime.m_lastMatchTime = 0; - - // Assert - assertEquals(0, robotTime.m_deltaTime); - assertEquals(0, robotTime.m_robotTime); - assertEquals(0, robotTime.m_lastRobotTime); - assertEquals(0, robotTime.m_frameNumber); - lastTime = robotTime.m_robotTime; - - // Act - wait(1); - robotTime.updateTimes(); - - // Assert - assertEquals(true, robotTime.m_deltaTime > 0); - assertEquals(true, robotTime.m_robotTime > 0); - assertEquals(lastTime, robotTime.m_lastRobotTime); - assertEquals(1, robotTime.m_frameNumber); - lastTime = robotTime.m_robotTime; - - // Act - wait(1); - robotTime.updateTimes(); - - // Assert - assertEquals(true, robotTime.m_deltaTime > 0); - assertEquals(true, robotTime.m_robotTime > 0); - assertEquals(lastTime, robotTime.m_lastRobotTime); - assertEquals(2, robotTime.m_frameNumber); - } - - @Test - public void testMatchTime() { - // Arrange - long lastTime; - - // Assert - assertEquals(0, robotTime.m_matchTime); - assertEquals(0, robotTime.m_lastMatchTime); - lastTime = robotTime.m_matchTime; - - // Act - robotTime.startMatchTime(); - wait(1); - robotTime.updateTimes(); - - // Assert - assertEquals(true, robotTime.m_matchTime > 0); - assertEquals(lastTime, robotTime.m_lastMatchTime); - lastTime = robotTime.m_matchTime; - - // Act - wait(1); - robotTime.updateTimes(); - robotTime.endMatchTime(); - - // Assert - assertEquals(0, robotTime.m_matchTime); - assertEquals(lastTime, robotTime.m_lastMatchTime); - lastTime = robotTime.m_matchTime; - - // Act - wait(1); - robotTime.updateTimes(); - - // Assert - assertEquals(0, robotTime.m_matchTime); - assertEquals(lastTime, robotTime.m_lastMatchTime); - } - - private void wait(int millis) { - try { - Thread.sleep(millis); - } catch (Exception e) {} - } -} From e9e06bf52d28d48d8d18f891e751d90e9144acc7 Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:45:15 -0700 Subject: [PATCH 66/70] port from 2023 --- src/main/java/frc4388/utility/AprilTag.java | 13 +++++++++++ .../java/frc4388/utility/DeferredBlock.java | 23 +++++++++++++++++++ .../java/frc4388/utility/UtilityStructs.java | 12 ++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/main/java/frc4388/utility/AprilTag.java create mode 100644 src/main/java/frc4388/utility/DeferredBlock.java create mode 100644 src/main/java/frc4388/utility/UtilityStructs.java diff --git a/src/main/java/frc4388/utility/AprilTag.java b/src/main/java/frc4388/utility/AprilTag.java new file mode 100644 index 0000000..c2ad269 --- /dev/null +++ b/src/main/java/frc4388/utility/AprilTag.java @@ -0,0 +1,13 @@ +package frc4388.utility; + +// This is a seperate class in case I want to encode rotation or other +// information about the tag +public class AprilTag { + public final double x, y, z; + + public AprilTag(double _x, double _y, double _z) { + x = _x; + y = _y; + z = _z; + } +} diff --git a/src/main/java/frc4388/utility/DeferredBlock.java b/src/main/java/frc4388/utility/DeferredBlock.java new file mode 100644 index 0000000..20d3c57 --- /dev/null +++ b/src/main/java/frc4388/utility/DeferredBlock.java @@ -0,0 +1,23 @@ +package frc4388.utility; + +import java.util.ArrayList; + +public class DeferredBlock { + private static ArrayList m_blocks = new ArrayList<>(); + private static boolean m_hasRun = false; + + public DeferredBlock(Runnable block) { + m_blocks.add(block); + } + + public static void execute() { + if (m_hasRun) return; + + for (Runnable block : m_blocks) { + block.run(); + } + + m_blocks.clear(); // for garbage collection + m_hasRun = true; + } +} diff --git a/src/main/java/frc4388/utility/UtilityStructs.java b/src/main/java/frc4388/utility/UtilityStructs.java new file mode 100644 index 0000000..e8b10cc --- /dev/null +++ b/src/main/java/frc4388/utility/UtilityStructs.java @@ -0,0 +1,12 @@ +package frc4388.utility; + +public class UtilityStructs { + public static class TimedOutput { + public double leftX = 0.0; + public double leftY = 0.0; + public double rightX = 0.0; + public double rightY = 0.0; + + public long timedOffset = 0; + } +} From d37e49a1058105199602c275b217396b16bc740c Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Fri, 5 Jan 2024 14:53:16 -0700 Subject: [PATCH 67/70] Vision Code --- src/main/java/frc4388/robot/Constants.java | 22 +++ .../frc4388/robot/subsystems/Apriltags.java | 36 ++++ .../frc4388/robot/subsystems/Limelight.java | 165 ++++++++++++++++++ .../java/frc4388/robot/subsystems/Vision.java | 38 ++++ vendordeps/photonlib.json | 42 +++++ 5 files changed, 303 insertions(+) create mode 100644 src/main/java/frc4388/robot/subsystems/Apriltags.java create mode 100644 src/main/java/frc4388/robot/subsystems/Limelight.java create mode 100644 src/main/java/frc4388/robot/subsystems/Vision.java create mode 100644 vendordeps/photonlib.json diff --git a/src/main/java/frc4388/robot/Constants.java b/src/main/java/frc4388/robot/Constants.java index 7077079..a58a1d0 100644 --- a/src/main/java/frc4388/robot/Constants.java +++ b/src/main/java/frc4388/robot/Constants.java @@ -111,6 +111,28 @@ public static final class Configurations { public static final int SMARTDASHBOARD_UPDATE_FRAME = 2; } + public static final class VisionConstants { + public static final String NAME = "photonCamera"; + + public static final int LIME_HIXELS = 640; + public static final int LIME_VIXELS = 480; + + public static final double H_FOV = 59.6; + public static final double V_FOV = 45.7; + + public static final double LIME_HEIGHT = 6.0; + public static final double LIME_ANGLE = 55.0; + + // public static final double HIGH_TARGET_HEIGHT = 46.0; + public static final double HIGH_TAPE_HEIGHT = 44.0; + + // public static final double MID_TARGET_HEIGHT = 34.0; + public static final double MID_TAPE_HEIGHT = 24.0; + + public static final double APRIL_HEIGHT = -1.0; // TODO: find actual value + + } + public static final class DriveConstants { public static final int DRIVE_PIGEON_ID = 6; diff --git a/src/main/java/frc4388/robot/subsystems/Apriltags.java b/src/main/java/frc4388/robot/subsystems/Apriltags.java new file mode 100644 index 0000000..c6062e8 --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/Apriltags.java @@ -0,0 +1,36 @@ +package frc4388.robot.subsystems; + +//import edu.wpi.first.apriltag.AprilTag; +//import edu.wpi.first.math.geometry.Pose3d; +//import edu.wpi.first.math.geometry.Rotation3d; +//import edu.wpi.first.networktables.NetworkTable; +//import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; + +public class Apriltags { + public static class Tag { + public boolean visible = true; + public double x, y, z = 0; + public double ry, rp, rr = 0; + } + + public Tag getTagPosRot() { + final var tagTable = NetworkTableInstance.getDefault().getTable("apriltag"); + + final Tag tag = new Tag(); + tag.visible = isAprilTag(); + tag.x = tagTable.getEntry("TagPosX").getDouble(0); + tag.y = tagTable.getEntry("TagPosY").getDouble(0); + tag.z = tagTable.getEntry("TagPosZ").getDouble(0); + tag.ry = tagTable.getEntry("TagRotY").getDouble(0); + tag.rp = tagTable.getEntry("TagRotP").getDouble(0); + tag.rr = tagTable.getEntry("TagRotR").getDouble(0); + + return tag; + } + + public boolean isAprilTag() { + final var tagTable = NetworkTableInstance.getDefault().getTable("apriltag"); + return tagTable.getEntry("IsTag").getBoolean(false); + } +} diff --git a/src/main/java/frc4388/robot/subsystems/Limelight.java b/src/main/java/frc4388/robot/subsystems/Limelight.java new file mode 100644 index 0000000..9d1289b --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/Limelight.java @@ -0,0 +1,165 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.subsystems; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.photonvision.EstimatedRobotPose; +import org.photonvision.PhotonCamera; +import org.photonvision.PhotonPoseEstimator; +import org.photonvision.PhotonPoseEstimator.PoseStrategy; +import org.photonvision.common.hardware.VisionLEDMode; +import org.photonvision.targeting.PhotonPipelineResult; +import org.photonvision.targeting.PhotonTrackedTarget; +import org.photonvision.targeting.TargetCorner; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.apriltag.AprilTagFieldLayout; +import edu.wpi.first.apriltag.AprilTagFields; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc4388.robot.Constants.VisionConstants; + +public class Limelight extends SubsystemBase { + + private PhotonCamera cam; + private PhotonPoseEstimator photonPoseEstimator; + + private boolean lightOn; + + /** Creates a new Limelight. */ + public Limelight() { + cam = new PhotonCamera(VisionConstants.NAME); + cam.setDriverMode(false); + } + + public void setLEDs(boolean on) { + lightOn = on; + cam.setLED(lightOn ? VisionLEDMode.kOn : VisionLEDMode.kOff); + } + + public void toggleLEDs() { + lightOn = !lightOn; + cam.setLED(lightOn ? VisionLEDMode.kOn : VisionLEDMode.kOff); + } + + public void setDriverMode(boolean driverMode) { + cam.setDriverMode(driverMode); + } + + public void setToLimePipeline() { + cam.setPipelineIndex(1); + setLEDs(true); + } + + public void setToAprilPipeline() { + cam.setPipelineIndex(0); + setLEDs(false); + } + + public PhotonTrackedTarget getAprilPoint() { + if (!cam.isConnected()) return null; + + PhotonPipelineResult result = cam.getLatestResult(); + + if (!result.hasTargets()) return null; + + return result.getBestTarget(); + } + + private List getAprilCorners() { + if (!cam.isConnected()) return null; + + PhotonPipelineResult result = cam.getLatestResult(); + + if (!result.hasTargets()) return null; + + return result.getBestTarget().getDetectedCorners(); + } + + public double getAprilSkew() { + List corners = getAprilCorners(); + ArrayList bottomSide = getAprilBottomSide(corners); + + if (bottomSide == null) return 0; + + TargetCorner bottomRight = bottomSide.get(0).x > bottomSide.get(1).x ? bottomSide.get(0) : bottomSide.get(1); + TargetCorner bottomLeft = bottomRight.x == bottomSide.get(0).x ? bottomSide.get(1) : bottomSide.get(0); + + return bottomLeft.y - bottomRight.y; + } + + private ArrayList getAprilBottomSide(List box) { + if (box == null) return null; + + ArrayList bottomSide = new ArrayList<>(); + + TargetCorner l1 = new TargetCorner(-1, -1); + TargetCorner l2 = new TargetCorner(-1, -1); + + for (TargetCorner c : box) { + if (c.y > l1.y) l1 = c; + } + + for (TargetCorner c : box) { + if (c.y == l1.y) continue; + if (c.y > l2.y) l2 = c; + } + + bottomSide.add(l1); + bottomSide.add(l2); + + return bottomSide; + } + + public double getDistanceToApril() { + PhotonTrackedTarget aprilPoint = getAprilPoint(); + if (aprilPoint == null) return -1; + + double aprilHeight = VisionConstants.APRIL_HEIGHT - VisionConstants.LIME_HEIGHT; + double theta = 35.0 + aprilPoint.getPitch(); + + double distanceToApril = aprilHeight / Math.tan(Math.toRadians(theta)); + return distanceToApril; + } + + public PhotonTrackedTarget getLowestTape() { + if (!cam.isConnected()) return null; + + PhotonPipelineResult result = cam.getLatestResult(); + + if (!result.hasTargets()) return null; + + ArrayList points = (ArrayList) result.getTargets(); + + PhotonTrackedTarget lowest = points.get(0); + for (PhotonTrackedTarget point : points) { + if (point.getPitch() < lowest.getPitch()) { + lowest = point; + } + } + + return lowest; + } + + public double getDistanceToTape() { + PhotonTrackedTarget tapePoint = getLowestTape(); + if (tapePoint == null) return -1; + + double tapeHeight = VisionConstants.MID_TAPE_HEIGHT - VisionConstants.LIME_HEIGHT; + double theta = 35.0 + tapePoint.getPitch(); + + double distanceToTape = tapeHeight / Math.tan(Math.toRadians(theta)); + return distanceToTape; + } + + @Override + public void periodic() {} +} diff --git a/src/main/java/frc4388/robot/subsystems/Vision.java b/src/main/java/frc4388/robot/subsystems/Vision.java new file mode 100644 index 0000000..371f621 --- /dev/null +++ b/src/main/java/frc4388/robot/subsystems/Vision.java @@ -0,0 +1,38 @@ +package frc4388.robot.subsystems; + +import edu.wpi.first.apriltag.AprilTag; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; + +public class Vision { + private final NetworkTableEntry m_isTags; + private final NetworkTableEntry m_xPoses; + private final NetworkTableEntry m_yPoses; + private final NetworkTableEntry m_zPoses; + + public Vision() { + final var tagTable = NetworkTableInstance.getDefault().getTable("apriltag"); + + m_isTags = tagTable.getEntry("IsTag"); + m_xPoses = tagTable.getEntry("TagPosX"); + m_yPoses = tagTable.getEntry("TagPosY"); + m_zPoses = tagTable.getEntry("TagPosZ"); + } + + public AprilTag[] getAprilTags() { + if (!m_isTags.getBoolean(false)) return new AprilTag[0]; + + double xarr[] = m_xPoses.getDoubleArray(new double[] {}); + double yarr[] = m_yPoses.getDoubleArray(new double[] {}); + double zarr[] = m_zPoses.getDoubleArray(new double[] {}); + + AprilTag tags[] = new AprilTag[xarr.length]; + for (int i = 0; i < tags.length; i++) { + tags[i] = new AprilTag(0, new Pose3d(xarr[i], yarr[i], zarr[i], new Rotation3d())); + } + + return tags; + } +} diff --git a/vendordeps/photonlib.json b/vendordeps/photonlib.json new file mode 100644 index 0000000..c940b75 --- /dev/null +++ b/vendordeps/photonlib.json @@ -0,0 +1,42 @@ +{ + "fileName": "photonlib.json", + "name": "photonlib", + "version": "v2024.1.1-beta-3.2", + "uuid": "515fe07e-bfc6-11fa-b3de-0242ac130004", + "frcYear": "2024", + "mavenUrls": [ + "https://maven.photonvision.org/repository/internal", + "https://maven.photonvision.org/repository/snapshots" + ], + "jsonUrl": "https://maven.photonvision.org/repository/internal/org/photonvision/PhotonLib-json/1.0/PhotonLib-json-1.0.json", + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "org.photonvision", + "artifactId": "PhotonLib-cpp", + "version": "v2024.1.1-beta-3.2", + "libName": "Photon", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxathena", + "linuxx86-64", + "osxuniversal" + ] + } + ], + "javaDependencies": [ + { + "groupId": "org.photonvision", + "artifactId": "PhotonLib-java", + "version": "v2024.1.1-beta-3.2" + }, + { + "groupId": "org.photonvision", + "artifactId": "PhotonTargeting-java", + "version": "v2024.1.1-beta-3.2" + } + ] +} \ No newline at end of file From afd18bd59fea4fc98238149763f1917be7ef16f4 Mon Sep 17 00:00:00 2001 From: 66945 <54561572+66945@users.noreply.github.com> Date: Sun, 7 Jan 2024 09:55:26 -0700 Subject: [PATCH 68/70] add deferred execution --- src/main/java/frc4388/robot/Robot.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index e145b38..980f662 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -68,6 +68,11 @@ public void disabledInit() { public void disabledPeriodic() { } + @Override + public void disabledExit() { + DeferredBlock.execute(); + } + /** * This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ From 93c97ea9126e0eafc427a689c6282c23248db30f Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:53:21 -0700 Subject: [PATCH 69/70] import Deferred Code --- src/main/java/frc4388/robot/Robot.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/frc4388/robot/Robot.java b/src/main/java/frc4388/robot/Robot.java index 980f662..e555b09 100644 --- a/src/main/java/frc4388/robot/Robot.java +++ b/src/main/java/frc4388/robot/Robot.java @@ -10,6 +10,7 @@ import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc4388.utility.DeferredBlock; import frc4388.utility.RobotTime; /** From c643ac0a2332e7ffb877f99cef1fcfcc028107cf Mon Sep 17 00:00:00 2001 From: Abhishrek05 <90010729+Abhishrek05@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:07:11 -0700 Subject: [PATCH 70/70] Add PID and Auto recording --- .../java/frc4388/robot/RobotContainer.java | 14 ++ .../robot/commands/Autos/PlaybackChooser.java | 91 +++++++ .../frc4388/robot/commands/Autos/Taxi.txt | 225 ++++++++++++++++++ src/main/java/frc4388/robot/commands/PID.java | 60 +++++ .../commands/Swerve/JoystickPlayback.java | 141 +++++++++++ .../commands/Swerve/JoystickRecorder.java | 97 ++++++++ .../robot/commands/Swerve/RotateToAngle.java | 35 +++ 7 files changed, 663 insertions(+) create mode 100644 src/main/java/frc4388/robot/commands/Autos/PlaybackChooser.java create mode 100644 src/main/java/frc4388/robot/commands/Autos/Taxi.txt create mode 100644 src/main/java/frc4388/robot/commands/PID.java create mode 100644 src/main/java/frc4388/robot/commands/Swerve/JoystickPlayback.java create mode 100644 src/main/java/frc4388/robot/commands/Swerve/JoystickRecorder.java create mode 100644 src/main/java/frc4388/robot/commands/Swerve/RotateToAngle.java diff --git a/src/main/java/frc4388/robot/RobotContainer.java b/src/main/java/frc4388/robot/RobotContainer.java index 99f5cb1..23c1b0f 100644 --- a/src/main/java/frc4388/robot/RobotContainer.java +++ b/src/main/java/frc4388/robot/RobotContainer.java @@ -13,6 +13,8 @@ import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.button.JoystickButton; import frc4388.robot.Constants.*; +import frc4388.robot.commands.Swerve.JoystickPlayback; +import frc4388.robot.commands.Swerve.JoystickRecorder; import frc4388.robot.subsystems.LED; import frc4388.utility.LEDPatterns; import frc4388.utility.controller.IHandController; @@ -57,6 +59,18 @@ public RobotContainer() { private void configureButtonBindings() { /* Driver Buttons */ // test command to spin the robot while pressing A on the driver controller + // new JoystickButton(getDeadbandedDriverController(), XboxController.RIGHT_BUMPER_BUTTON) + // .whileTrue(new JoystickRecorder(m_robotSwerveDrive, + // () -> getDeadbandedDriverController().getLeftX(), + // () -> getDeadbandedDriverController().getLeftY(), + // () -> getDeadbandedDriverController().getRightX(), + // () -> getDeadbandedDriverController().getRightY(), + // "Blue1Path.txt")) + // .onFalse(new InstantCommand()); + + // new JoystickButton(getDeadbandedDriverController(), XboxController.LEFT_BUMPER_BUTTON) + // .onTrue(new JoystickPlayback(m_robotSwerveDrive, "Blue1Path.txt")) + // .onFalse(new InstantCommand()); /* Operator Buttons */ // activates "Lit Mode" diff --git a/src/main/java/frc4388/robot/commands/Autos/PlaybackChooser.java b/src/main/java/frc4388/robot/commands/Autos/PlaybackChooser.java new file mode 100644 index 0000000..2438a38 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Autos/PlaybackChooser.java @@ -0,0 +1,91 @@ +package frc4388.robot.commands.Autos; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; + +import edu.wpi.first.wpilibj.shuffleboard.BuiltInWidgets; +import edu.wpi.first.wpilibj.shuffleboard.ComplexWidget; +import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc4388.robot.commands.Swerve.JoystickPlayback; +import frc4388.robot.subsystems.SwerveDrive; + +public class PlaybackChooser { + private final ArrayList> m_choosers = new ArrayList<>(); + private SendableChooser m_playback = null; + private final ArrayList m_widgets = new ArrayList<>(); + private final HashMap m_commandPool = new HashMap<>(); + + private final File m_dir = new File("/home/lvuser/autos/"); + private int m_cmdNum = 0; + private final SwerveDrive m_swerve; + + // commands + private Command m_noAuto = new InstantCommand(); + + public PlaybackChooser(SwerveDrive swerve, Object... pool) { + m_swerve = swerve; + } + + public PlaybackChooser addOption(String name, Command option) { + m_commandPool.put(name, option); + return this; + } + + public PlaybackChooser buildDisplay() { + for (int i = 0; i < 10; i++) { + appendCommand(); + } + m_playback = m_choosers.get(0); + nextChooser(); + + Shuffleboard.getTab("Auto Chooser") + .add("Add Sequence", new InstantCommand(() -> nextChooser())) + .withPosition(4, 0); + return this; + } + + // This will be bound to a button for the time being + public void appendCommand() { + var chooser = new SendableChooser(); + chooser.setDefaultOption("No Auto", m_noAuto); + + m_choosers.add(chooser); + ComplexWidget widget = Shuffleboard.getTab("Auto Chooser") + .add("Command: " + m_choosers.size(), chooser) + .withSize(4, 1) + .withPosition(0, m_choosers.size() - 1) + .withWidget(BuiltInWidgets.kSplitButtonChooser); + + m_widgets.add(widget); + } + + public void nextChooser() { + SendableChooser chooser = m_choosers.get(m_cmdNum++); + + for (String auto : m_dir.list()) { + chooser.addOption(auto, new JoystickPlayback(m_swerve, auto)); + } + for (var cmd_name : m_commandPool.keySet()) { + chooser.addOption(cmd_name, m_commandPool.get(cmd_name)); + } + } + + public Command getCommand() { + Command command = m_playback.getSelected(); + command = command == null ? m_noAuto : command.asProxy(); + + Command[] commands = new Command[m_cmdNum - 1]; + for (int i = 0; i < m_cmdNum - 1; i++) { + Command command2 = m_choosers.get(i + 1).getSelected(); + command2 = command2 == null ? m_noAuto : command2.asProxy(); + + commands[i] = command2.asProxy(); + } + + return command.andThen(commands); + } +} diff --git a/src/main/java/frc4388/robot/commands/Autos/Taxi.txt b/src/main/java/frc4388/robot/commands/Autos/Taxi.txt new file mode 100644 index 0000000..c99ee2c --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Autos/Taxi.txt @@ -0,0 +1,225 @@ +0.0,0.0,0.0,0.0,0 +0.0,0.0,0.0,0.0,0 +0.0,0.0,0.0,0.0,12 +0.0,0.0,0.0,0.0,26 +0.0,0.0,0.0,0.0,37 +0.0,0.0,0.0,0.0,50 +0.0,0.0,0.0,0.0,62 +0.0,0.0,0.0,0.0,73 +0.0,0.0,0.0,0.0,88 +0.0,0.0,0.0,0.0,103 +0.0,0.0,0.0,0.0,116 +0.0,0.0,0.0,0.0,160 +0.0,0.0,0.0,0.0,173 +0.0,0.0,0.0,0.0,185 +0.0,0.0,0.0,0.0,200 +0.0,0.0,0.0,0.0,211 +0.0,0.0,0.0,0.0,223 +0.0,0.0,0.0,0.0,235 +0.0,0.0,0.0,0.0,247 +0.0,0.0,0.0,0.0,263 +0.0,0.0,0.0,0.0,283 +0.0,0.0,0.0,0.0,303 +0.0,-0.109375,0.0,0.0,323 +0.0,-0.1484375,0.0,0.0,343 +0.0,-0.2109375,0.0,0.0,363 +0.0,-0.3671875,0.0,0.0,398 +0.0,-0.4140625,0.0,0.0,411 +0.0,-0.4765625,0.0,0.0,425 +0.0,-0.5078125,0.0,0.0,443 +0.0,-0.5078125,0.0,0.0,463 +0.0,-0.53125,0.0,0.0,483 +0.0,-0.5546875,0.0,0.0,504 +0.0,-0.5625,0.0,0.0,523 +0.0,-0.5625,0.0,0.0,544 +0.0,-0.5703125,0.0,0.0,563 +0.0,-0.5859375,0.0,0.0,584 +0.0,-0.5859375,0.0,0.0,603 +0.0,-0.5859375,0.0,0.0,640 +0.0,-0.59375,0.0,0.0,657 +0.0,-0.6015625,0.0,0.0,672 +0.0,-0.6015625,0.0,0.0,685 +0.0,-0.6015625,0.0,0.0,703 +0.0,-0.6015625,0.0,0.0,723 +0.0,-0.6015625,0.0,0.0,743 +0.0,-0.6015625,0.0,0.0,763 +0.0,-0.6015625,0.0,0.0,783 +0.0,-0.6015625,0.0,0.0,803 +0.0,-0.6015625,0.0,0.0,823 +0.0,-0.6015625,0.0,0.0,844 +0.0,-0.6015625,0.0,0.0,878 +0.0,-0.6015625,0.0,0.0,893 +0.0,-0.6015625,0.0,0.0,907 +0.0,-0.6015625,0.0,0.0,924 +0.0,-0.609375,0.0,0.0,943 +0.0,-0.609375,0.0,0.0,963 +0.0,-0.609375,0.0,0.0,983 +0.0,-0.609375,0.0,0.0,1004 +0.0,-0.609375,0.0,0.0,1023 +0.0,-0.609375,0.0,0.0,1043 +0.0,-0.609375,0.0,0.0,1064 +0.0,-0.609375,0.0,0.0,1083 +0.0,-0.609375,0.0,0.0,1156 +0.0,-0.609375,0.0,0.0,1172 +0.0,-0.609375,0.0,0.0,1185 +0.0,-0.609375,0.0,0.0,1200 +0.0,-0.609375,0.0,0.0,1215 +0.0,-0.609375,0.0,0.0,1225 +0.0,-0.609375,0.0,0.0,1236 +0.0,-0.609375,0.0,0.0,1249 +0.0,-0.609375,0.0,0.0,1263 +0.0,-0.609375,0.0,0.0,1283 +0.0,-0.609375,0.0,0.0,1303 +0.0,-0.609375,0.0,0.0,1323 +0.0,-0.609375,0.0,0.0,1363 +0.0,-0.6015625,0.0,0.0,1376 +0.0,-0.6015625,0.0,0.0,1394 +0.0,-0.6015625,0.0,0.0,1405 +0.0,-0.6015625,0.0,0.0,1423 +0.0,-0.6015625,0.0,0.0,1443 +0.0,-0.6015625,0.0,0.0,1463 +0.0,-0.6015625,0.0,0.0,1483 +0.0,-0.6015625,0.0,0.0,1503 +0.0,-0.6015625,0.0,0.0,1523 +0.0,-0.6015625,0.0,0.0,1543 +0.0,-0.6015625,0.0,0.0,1563 +0.0,-0.6015625,0.0,0.0,1597 +0.0,-0.6015625,0.0,0.0,1608 +0.0,-0.6015625,0.0,0.0,1624 +0.0,-0.6015625,0.0,0.0,1643 +0.0,-0.6015625,0.0,0.0,1664 +0.0,-0.5859375,0.0,0.0,1683 +0.0,-0.5859375,0.0,0.0,1703 +0.0,-0.5625,0.0,0.0,1723 +0.0,-0.5625,0.0,0.0,1743 +0.0,-0.5625,0.0,0.0,1763 +0.0,-0.5625,0.0,0.0,1783 +0.0,-0.5625,0.0,0.0,1803 +0.0,-0.5625,0.0,0.0,1843 +0.0,-0.5625,0.0,0.0,1855 +0.0,-0.5625,0.0,0.0,1868 +0.0,-0.5625,0.0,0.0,1883 +0.0,-0.5625,0.0,0.0,1903 +0.0,-0.5625,0.0,0.0,1923 +0.0,-0.5625,0.0,0.0,1943 +0.0,-0.5625,0.0,0.0,1963 +0.0,-0.5625,0.0,0.0,1983 +0.0,-0.5625,0.0,0.0,2003 +0.0,-0.5625,0.0,0.0,2024 +0.0,-0.5625,0.0,0.0,2043 +0.0,-0.5625,0.0,0.0,2081 +0.0,-0.5625,0.0,0.0,2093 +0.0,-0.5625,0.0,0.0,2105 +0.0,-0.5625,0.0,0.0,2123 +0.0,-0.5625,0.0,0.0,2143 +0.0,-0.5625,0.0,0.0,2163 +0.0,-0.5625,0.0,0.0,2183 +0.0,-0.5625,0.0,0.0,2203 +0.0,-0.5625,0.0,0.0,2223 +0.0,-0.5625,0.0,0.0,2243 +0.0,-0.5625,0.0,0.0,2263 +0.0,-0.5625,0.0,0.0,2283 +0.0,-0.5625,0.0,0.0,2366 +0.0,-0.5625,0.0,0.0,2377 +0.0,-0.5625,0.0,0.0,2394 +0.0,-0.5703125,0.0,0.0,2405 +0.0,-0.5703125,0.0,0.0,2418 +0.0,-0.5703125,0.0,0.0,2431 +0.0,-0.5703125,0.0,0.0,2444 +0.0,-0.5703125,0.0,0.0,2458 +0.0,-0.5703125,0.0,0.0,2470 +0.0,-0.5703125,0.0,0.0,2485 +0.0,-0.5703125,0.0,0.0,2503 +0.0,-0.5703125,0.0,0.0,2523 +0.0,-0.5703125,0.0,0.0,2563 +0.0,-0.5703125,0.0,0.0,2577 +0.0,-0.5703125,0.0,0.0,2591 +0.0,-0.5703125,0.0,0.0,2608 +0.0,-0.5703125,0.0,0.0,2624 +0.0,-0.5703125,0.0,0.0,2643 +0.0,-0.5703125,0.0,0.0,2677 +0.0,-0.5703125,0.0,0.0,2698 +0.0,-0.5703125,0.0,0.0,2711 +0.0,-0.5703125,0.0,0.0,2725 +0.0,-0.5703125,0.0,0.0,2743 +0.0,-0.5703125,0.0,0.0,2764 +0.0,-0.5703125,0.0,0.0,2810 +0.0,-0.5703125,0.0,0.0,2820 +0.0,-0.5703125,0.0,0.0,2833 +0.0,-0.5703125,0.0,0.0,2845 +0.0,-0.5703125,0.0,0.0,2863 +0.0,-0.5703125,0.0,0.0,2883 +0.0,-0.5703125,0.0,0.0,2904 +0.0,-0.5703125,0.0,0.0,2924 +0.0,-0.5703125,0.0,0.0,2943 +0.0,-0.5703125,0.0,0.0,2963 +0.0,-0.5703125,0.0,0.0,2983 +0.0,-0.5703125,0.0,0.0,3003 +0.0,-0.5703125,0.0,0.0,3033 +0.0,-0.5703125,0.0,0.0,3050 +0.0,-0.5703125,0.0,0.0,3065 +0.0,-0.5703125,0.0,0.0,3083 +0.0,-0.5703125,0.0,0.0,3103 +0.0,-0.5703125,0.0,0.0,3123 +0.0,-0.5703125,0.0,0.0,3144 +0.0,-0.5703125,0.0,0.0,3164 +0.0,-0.5703125,0.0,0.0,3184 +0.0,-0.5703125,0.0,0.0,3203 +0.0,-0.5703125,0.0,0.0,3223 +0.0,-0.5703125,0.0,0.0,3243 +0.0,-0.5703125,0.0,0.0,3272 +0.0,-0.5703125,0.0,0.0,3289 +0.0,-0.5703125,0.0,0.0,3303 +0.0,-0.5703125,0.0,0.0,3323 +0.0,-0.5703125,0.0,0.0,3343 +0.0,-0.5703125,0.0,0.0,3363 +0.0,-0.5703125,0.0,0.0,3383 +0.0,-0.5703125,0.0,0.0,3403 +0.0,-0.5703125,0.0,0.0,3423 +0.0,-0.5703125,0.0,0.0,3443 +0.0,-0.5703125,0.0,0.0,3463 +0.0,-0.5703125,0.0,0.0,3483 +0.0,-0.5703125,0.0,0.0,3566 +0.0,-0.5703125,0.0,0.0,3578 +0.0,-0.5703125,0.0,0.0,3596 +0.0,-0.5703125,0.0,0.0,3610 +0.0,-0.5703125,0.0,0.0,3623 +0.0,-0.5703125,0.0,0.0,3640 +0.0,-0.5703125,0.0,0.0,3651 +0.0,-0.5703125,0.0,0.0,3663 +0.0,-0.5703125,0.0,0.0,3678 +0.0,-0.5703125,0.0,0.0,3691 +0.0,-0.5703125,0.0,0.0,3706 +0.0,-0.5703125,0.0,0.0,3723 +0.0,-0.5703125,0.0,0.0,3766 +0.0,-0.5703125,0.0,0.0,3778 +0.0,-0.5703125,0.0,0.0,3792 +0.0,-0.5703125,0.0,0.0,3807 +0.0,-0.5703125,0.0,0.0,3823 +0.0,-0.5703125,0.0,0.0,3843 +0.0,-0.53125,0.0,0.0,3863 +0.0,-0.53125,0.0,0.0,3884 +0.0,-0.421875,0.0,0.0,3904 +0.0,0.0,0.0,0.0,3924 +0.0,0.0,0.0,0.0,3944 +0.0,0.0,0.0,0.0,3963 +0.0,0.0,0.0,0.0,3999 +0.0,0.0,0.0,0.0,4010 +0.0,0.0,0.0,0.0,4025 +0.0,0.0,0.0,0.0,4043 +0.0,0.0,0.0,0.0,4063 +0.0,0.0,0.0,0.0,4083 +0.0,0.0,0.0,0.0,4103 +0.0,0.0,0.0,0.0,4123 +0.0,0.0,0.0,0.0,4143 +0.0,0.0,0.0,0.0,4163 +0.0,0.0,0.0,0.0,4183 +0.0,0.0,0.0,0.0,4203 +0.0,0.0,0.0,0.0,4236 +0.0,0.0,0.0,0.0,4247 +0.0,0.0,0.0,0.0,4264 +0.0,0.0,0.0,0.0,4284 +0.0,0.0,0.0,0.0,4304 +0.0,0.0,0.0,0.0,4324 +0.0,0.0,0.0,0.0,4343 +0.0,0.0,0.0,0.0,4363 \ No newline at end of file diff --git a/src/main/java/frc4388/robot/commands/PID.java b/src/main/java/frc4388/robot/commands/PID.java new file mode 100644 index 0000000..c592c73 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/PID.java @@ -0,0 +1,60 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.commands; + +import edu.wpi.first.wpilibj2.command.CommandBase; +import frc4388.utility.Gains; + +public abstract class PID extends CommandBase { + protected Gains gains; + private double output = 0; + private double tolerance = 0; + + /** Creates a new PelvicInflammatoryDisease. */ + public PID(double kp, double ki, double kd, double kf, double tolerance) { + gains = new Gains(kp, ki, kd, kf, 0); + this.tolerance = tolerance; + } + + public PID(Gains gains, double tolerance) { + this.gains = gains; + this.tolerance = tolerance; + } + + /** produces the error from the setpoint */ + public abstract double getError(); + + /** figure it out bitch */ + public abstract void runWithOutput(double output); + + // Called when the command is initially scheduled. + @Override + public final void initialize() { + output = 0; + } + + private double prevError, cumError = 0; + + // Called every time the scheduler runs while the command is scheduled. + @Override + public final void execute() { + double error = getError(); + cumError += error * .02; // 20 ms + double delta = error - prevError; + + output = error * gains.kP; + output += cumError * gains.kI; + output += delta * gains.kD; + output += gains.kF; + + runWithOutput(output); + } + + // Returns true when the command should end. + @Override + public final boolean isFinished() { + return Math.abs(getError()) < tolerance; + } +} diff --git a/src/main/java/frc4388/robot/commands/Swerve/JoystickPlayback.java b/src/main/java/frc4388/robot/commands/Swerve/JoystickPlayback.java new file mode 100644 index 0000000..56e8d43 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Swerve/JoystickPlayback.java @@ -0,0 +1,141 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.commands.Swerve; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Scanner; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.wpilibj2.command.CommandBase; +import frc4388.robot.subsystems.SwerveDrive; +import frc4388.utility.UtilityStructs.TimedOutput; + +public class JoystickPlayback extends CommandBase { + private final SwerveDrive swerve; + private String filename; + private int mult = 1; + private Scanner input; + private final ArrayList outputs = new ArrayList<>(); + private int counter = 0; + private long startTime = 0; + private long playbackTime = 0; + private int lastIndex; + private boolean m_finished = false; // ! find a better way + + /** Creates a new JoystickPlayback. */ + public JoystickPlayback(SwerveDrive swerve, String filename, int mult) { + this.swerve = swerve; + this.filename = filename; + this.mult = mult; + + addRequirements(this.swerve); + } + + /** Creates a new JoystickPlayback. */ + public JoystickPlayback(SwerveDrive swerve, String filename) { + this(swerve, filename, 1); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + outputs.clear(); + m_finished = false; + + startTime = System.currentTimeMillis(); + playbackTime = 0; + lastIndex = 0; + try { + input = new Scanner(new File("/home/lvuser/autos/" + filename)); + + String line = ""; + while (input.hasNextLine()) { + line = input.nextLine(); + + if (line.isEmpty() || line.isBlank() || line.equals("\n")) { + continue; + } + + String[] values = line.split(","); + System.out.println("values: " + values[0] + " " + values[1] + " " + values[2] + " " + values[3]); + + var out = new TimedOutput(); + out.leftX = Double.parseDouble(values[0]) * mult; + out.leftY = Double.parseDouble(values[1]); + out.rightX = Double.parseDouble(values[2]); + out.rightY = Double.parseDouble(values[3]); + + out.timedOffset = Long.parseLong(values[4]); + + outputs.add(out); + } + + input.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + if (counter == 0) { + startTime = System.currentTimeMillis(); + playbackTime = 0; + } else { + playbackTime = System.currentTimeMillis() - startTime; + } + + // skip to reasonable time frame + // too tired to write comment: ask daniel thomas; it goes to the thing until it's bigger than the other thing + { + int i = lastIndex == 0 ? 1 : lastIndex; + while (i < outputs.size() && outputs.get(i).timedOffset < playbackTime) { + i++; + } + + if (i >= outputs.size()) { + m_finished = true; // ! kind of a hack + return; + } + lastIndex = i; + } + + TimedOutput lastOut = outputs.get(lastIndex - 1); + TimedOutput out = outputs.get(lastIndex); + + double deltaTime = out.timedOffset - lastOut.timedOffset; + double playbackDelta = playbackTime - lastOut.timedOffset; + + double lerpLX = lastOut.leftX + (out.leftX - lastOut.leftX) * (playbackDelta / deltaTime); + double lerpLY = lastOut.leftY + (out.leftY - lastOut.leftY) * (playbackDelta / deltaTime); + double lerpRX = lastOut.rightX + (out.rightX - lastOut.rightX) * (playbackDelta / deltaTime); + double lerpRY = lastOut.rightY + (out.rightY - lastOut.rightY) * (playbackDelta / deltaTime); + + // this.swerve.driveWithInput(new Translation2d(out.leftX, out.leftY), + // new Translation2d(out.rightX, out.rightY), + // true); + + this.swerve.driveWithInput( new Translation2d(lerpLX, lerpLY), + new Translation2d(lerpRX, lerpRY), + true); + + counter++; + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + input.close(); + swerve.stopModules(); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + return m_finished; + } +} diff --git a/src/main/java/frc4388/robot/commands/Swerve/JoystickRecorder.java b/src/main/java/frc4388/robot/commands/Swerve/JoystickRecorder.java new file mode 100644 index 0000000..84608cc --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Swerve/JoystickRecorder.java @@ -0,0 +1,97 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.commands.Swerve; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.function.Supplier; + +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.wpilibj2.command.CommandBase; +import frc4388.robot.subsystems.SwerveDrive; +import frc4388.utility.UtilityStructs.TimedOutput; + +public class JoystickRecorder extends CommandBase { + public final SwerveDrive swerve; + + public final Supplier leftX; + public final Supplier leftY; + public final Supplier rightX; + public final Supplier rightY; + private String filename; + public final ArrayList outputs = new ArrayList<>(); + private long startTime = -1; + + + /** Creates a new JoystickRecorder. */ + public JoystickRecorder(SwerveDrive swerve, Supplier leftX, Supplier leftY, + Supplier rightX, Supplier rightY, + String filename) + { + this.swerve = swerve; + this.leftX = leftX; + this.leftY = leftY; + this.rightX = rightX; + this.rightY = rightY; + this.filename = filename; + + addRequirements(this.swerve); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + outputs.clear(); + + this.startTime = System.currentTimeMillis(); + + outputs.add(new TimedOutput()); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + var inputs = new TimedOutput(); + inputs.leftX = leftX.get(); + inputs.leftY = leftY.get(); + inputs.rightX = rightX.get(); + inputs.rightY = rightY.get(); + inputs.timedOffset = System.currentTimeMillis() - startTime; + + outputs.add(inputs); + + swerve.driveWithInput(new Translation2d(inputs.leftX, inputs.leftY), + new Translation2d(inputs.rightX, inputs.rightY), + true); + + System.out.println("RECORDING"); + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + File output = new File("/home/lvuser/autos/" + filename); + + try (PrintWriter writer = new PrintWriter(output)) { + for (var input : outputs) { + writer.println( input.leftX + "," + input.leftY + "," + + input.rightX + "," + input.rightY + "," + + input.timedOffset); + } + + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/src/main/java/frc4388/robot/commands/Swerve/RotateToAngle.java b/src/main/java/frc4388/robot/commands/Swerve/RotateToAngle.java new file mode 100644 index 0000000..a2945c0 --- /dev/null +++ b/src/main/java/frc4388/robot/commands/Swerve/RotateToAngle.java @@ -0,0 +1,35 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package frc4388.robot.commands.Swerve; + +import edu.wpi.first.math.geometry.Translation2d; +import frc4388.robot.commands.PID; +import frc4388.robot.subsystems.SwerveDrive; + +public class RotateToAngle extends PID { + + SwerveDrive drive; + double targetAngle; + + /** Creates a new RotateToAngle. */ + public RotateToAngle(SwerveDrive drive, double targetAngle) { + super(0.3, 0.0, 0.0, 0.0, 1); + + this.drive = drive; + this.targetAngle = targetAngle; + + addRequirements(drive); + } + + @Override + public double getError() { + return targetAngle - drive.getGyroAngle(); + } + + @Override + public void runWithOutput(double output) { + drive.driveWithInput(new Translation2d(0.0, 0.0), new Translation2d(output / Math.abs(getError()), 0.0), true); + } +}