diff --git a/.gitignore b/.gitignore index 524f096..2070218 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* +.DS_Store diff --git a/2024-Crescendo/documentation/2024 Robot User Manual.pdf b/2024-Crescendo/documentation/2024 Robot User Manual.pdf index 9443c10..fad6144 100644 Binary files a/2024-Crescendo/documentation/2024 Robot User Manual.pdf and b/2024-Crescendo/documentation/2024 Robot User Manual.pdf differ diff --git a/2024-Crescendo/src/main/java/frc/robot/Constants.java b/2024-Crescendo/src/main/java/frc/robot/Constants.java index c076ba2..8f82446 100644 --- a/2024-Crescendo/src/main/java/frc/robot/Constants.java +++ b/2024-Crescendo/src/main/java/frc/robot/Constants.java @@ -14,8 +14,8 @@ */ public final class Constants { public static class RobotConstants { - public static final double minPnuematicsPressure = 80.0; - public static final double maxPnuematicsPressure = 120.0; + public static final double minPnuematicsPressure = 80.0; // default 80.0 + public static final double maxPnuematicsPressure = 120.0; // default 120.0 public static final int periodicTicksPerSecond = 50; public static final int pnuematicReportingFreq = 1; @@ -36,19 +36,16 @@ public static class DrivetrainConstants { } public static class DriverConstants { - public static final int kLeftDriveJoystick = 0; - public static final int kRightDriveJoystick = 1; + public static final int kDriveJoystick = 0; public static final double kTurnDivider = 2.0; public static final double kLowGearDivider = 2.0; // Left Joystick public static final int kLowGear = 1; // Joystick Trigger - public static final int kReverseMode = 9; // Joystick button 9 + public static final int kReverseMode = 11; // Joystick button 11 public static final int kForwardMode = 7; // Joystick button 7 - - // Right Joystick - public static final int kDriveStraight = 1; // Joystick Trigger + } public static class OperatorConstants { @@ -69,8 +66,8 @@ public static class OperatorConstants { public static final int kSpitOut = 10; // right joystick down public static final double kGooseAngleIncrement = 1.0; - public static final double kGooseAngleMax = 130.0; - public static final double kGooseAngleMin = -10.0; + public static final double kGooseAngleMax = 120.0; + public static final double kGooseAngleMin = 0.0; } public static class ClimbConstants { @@ -100,6 +97,7 @@ public static class NoteHandlingConstants { public static class GooseRotationConstants { public static final int kRotateMotorCANID = 40; + public static final int kSmartMotionSlot = 0; // TODO: Need to tune // SparkMax PID coefficients for the rotation controller. @@ -112,21 +110,28 @@ public static class GooseRotationConstants { public static final double kMinOutput = -1.0; public static final double kMaxOutput = 1.0; + public static final double kMaxVelocity = 1500.0; + public static final double kMinVelocity = 0.0; + public static final double kMaxAcceleration = 500.0; + public static final double kMaxError = 0.1; + + public static final boolean kUseSmartMotion = false; + // Starting in a non-end positon, there will be a switch at one or both of the end positions. // When the switch(s) are hit the software will reset to the known position. // Currently we are presuming there will be a thing to set the arm at the start of the game. // TODO: The next 7 values are guesses and need to be measured. public static final double kStartingAngle = 30.0; - public static final double kBottomRawEncoderValue = 0.680; - public static final double kBottomAngle = -10.0; + public static final double kBottomRawEncoderValue = 0.523; + public static final double kBottomAngle = 0.0; - public static final double kTopRawEncoderValue = 0.386; - public static final double kTopAngle = 115.0; + public static final double kTopRawEncoderValue = 0.205; + public static final double kTopAngle = 115.0; public static final double kScoreAngle = 130.0; public static final double kMiddleAngle = 40.0; - public static final double kIntakeAngle = -5.7; + public static final double kIntakeAngle = 0.0; // This defines the gear ratio between the motor and output shafts. Divide // the motor rotation count by this number to determine the output shaft diff --git a/2024-Crescendo/src/main/java/frc/robot/RobotContainer.java b/2024-Crescendo/src/main/java/frc/robot/RobotContainer.java index 4aa7933..4b2607e 100644 --- a/2024-Crescendo/src/main/java/frc/robot/RobotContainer.java +++ b/2024-Crescendo/src/main/java/frc/robot/RobotContainer.java @@ -11,7 +11,6 @@ import frc.robot.commands.ClimbCommand; import frc.robot.commands.NoteHandlingSpeedCommand; import frc.robot.commands.RotateCommand; -import frc.robot.commands.SetDriveStraightCommand; import frc.robot.commands.SetGearCommand; import frc.robot.commands.SetModeCommand; import frc.robot.subsystems.DriveSubsystem; @@ -38,7 +37,6 @@ public class RobotContainer { // Create SmartDashboard chooser for autonomous routines private final SendableChooser m_autoChooser = new SendableChooser<>(); - private final SendableChooser m_driveTypeChooser = new SendableChooser<>(); // The robot's subsystems and commands are defined here... private final PneumaticHub m_pnuematicHub = new PneumaticHub(); @@ -49,20 +47,16 @@ public class RobotContainer { // Joysticks private final XboxController m_operatorController = new XboxController(OperatorConstants.kOperatorController); - private final Joystick m_driverJoystickLeft = new Joystick(DriverConstants.kLeftDriveJoystick); - private final Joystick m_driverJoystickRight = new Joystick(DriverConstants.kRightDriveJoystick); + private final Joystick m_driverJoystick = new Joystick(DriverConstants.kDriveJoystick); // Driver Buttons public final JoystickButton m_lowGearButton = - new JoystickButton(m_driverJoystickLeft, DriverConstants.kLowGear); + new JoystickButton(m_driverJoystick, DriverConstants.kLowGear); public final JoystickButton m_forwardModeButton = - new JoystickButton(m_driverJoystickLeft, DriverConstants.kForwardMode); + new JoystickButton(m_driverJoystick, DriverConstants.kForwardMode); public final JoystickButton m_reverseModeButton = - new JoystickButton(m_driverJoystickLeft, DriverConstants.kReverseMode); - - public final JoystickButton m_driveStraightButton = - new JoystickButton(m_driverJoystickRight, DriverConstants.kDriveStraight); + new JoystickButton(m_driverJoystick, DriverConstants.kReverseMode); // Pnuematics Climb Buttons private final JoystickButton m_climbUpButton = @@ -112,12 +106,6 @@ public void reportStatus() { SmartDashboard.putBoolean("Compressor Running", m_pnuematicHub.getCompressor()); } - if(m_tickCount % (RobotConstants.periodicTicksPerSecond/RobotConstants.imuReportingFreq) == 0) - { - // Report IMU state. - SmartDashboard.putNumber("Heading", m_driveSubsystem.getHeading()); - } - if(m_tickCount % (RobotConstants.periodicTicksPerSecond/RobotConstants.gooseReportingFreq) == 0) { // Report Goose ARM state. @@ -139,28 +127,18 @@ private void configureDriverStationControls() m_autoChooser.addOption("Drive forward 4 Seconds", new AutonomousDrive4Sec(m_driveSubsystem)); m_autoChooser.addOption("Drive forward 6 Seconds", new AutonomousDrive6Sec(m_driveSubsystem)); SmartDashboard.putData(m_autoChooser); - - // Drive control option selector - m_driveTypeChooser.setDefaultOption("Arcade Drive", true); - m_driveTypeChooser.addOption("Tank Drive", false); - SmartDashboard.putData(m_driveTypeChooser); } private void configureBindings() { // Drive Controllers. We set arcade drive as the default here but may change this // during teleopInit depending upon the value of a dashboard chooser. - m_driveSubsystem.initDefaultCommand(m_driverJoystickLeft, m_driverJoystickRight, true); - m_lowGearButton.onTrue(new SetGearCommand(m_driveSubsystem, true)); m_lowGearButton.onFalse(new SetGearCommand(m_driveSubsystem, false)); m_forwardModeButton.onTrue(new SetModeCommand(m_driveSubsystem, false)); m_reverseModeButton.onTrue(new SetModeCommand(m_driveSubsystem, true)); - m_driveStraightButton.onTrue(new SetDriveStraightCommand(m_driveSubsystem, true)); - m_driveStraightButton.onFalse(new SetDriveStraightCommand(m_driveSubsystem, false)); - // Climb Buttons m_climbUpButton.onTrue(new ClimbCommand(m_climbSubsystem, ClimbSubsystem.State.LIFTED)); m_climbDownButton.onTrue(new ClimbCommand(m_climbSubsystem, ClimbSubsystem.State.GROUNDED)); @@ -206,11 +184,9 @@ public Command getAutonomousCommand() { return m_autoChooser.getSelected(); } - // Set the driver's choice of control mode based on the selection provided - // in a dashboard control. + // Set drive command to arcade public void setDriveType() { - Boolean bArcade = m_driveTypeChooser.getSelected(); - m_driveSubsystem.initDefaultCommand(m_driverJoystickLeft, m_driverJoystickRight, bArcade); + m_driveSubsystem.initDefaultCommand(m_driverJoystick); } } diff --git a/2024-Crescendo/src/main/java/frc/robot/subsystems/DriveSubsystem.java b/2024-Crescendo/src/main/java/frc/robot/subsystems/DriveSubsystem.java index 1756438..3a94f54 100644 --- a/2024-Crescendo/src/main/java/frc/robot/subsystems/DriveSubsystem.java +++ b/2024-Crescendo/src/main/java/frc/robot/subsystems/DriveSubsystem.java @@ -5,17 +5,15 @@ package frc.robot.subsystems; import frc.robot.Constants.*; -import frc.robot.commands.TankDriveCommand; import frc.robot.commands.ArcadeDriveCommand; import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj.SPI; import com.ctre.phoenix6.controls.Follower; import com.ctre.phoenix6.hardware.TalonFX; -import com.kauailabs.navx.frc.AHRS; +import com.ctre.phoenix6.signals.NeutralModeValue; public class DriveSubsystem extends SubsystemBase { @@ -23,11 +21,10 @@ public class DriveSubsystem extends SubsystemBase private final TalonFX m_leftBackMotor = new TalonFX(DrivetrainConstants.kLeftBackMotorCANID); private final TalonFX m_rightFrontMotor = new TalonFX(DrivetrainConstants.kRightFrontMotorCANID); private final TalonFX m_rightBackMotor = new TalonFX(DrivetrainConstants.kRightBackMotorCANID); - private final AHRS m_NavX = new AHRS(SPI.Port.kMXP); private DifferentialDrive m_Drivetrain; private double m_leftSpeed = 0.0; private double m_rightSpeed = 0.0; - private double m_speedDivider = 1.0; + private double m_speedDivider = 2.5; // Default 1.0 private double m_modeMultiplier = 1.0; private boolean m_driveStraight = false; @@ -42,28 +39,24 @@ public DriveSubsystem() m_rightFrontMotor.setInverted(true); m_rightBackMotor.setInverted(true); + // Set all motors to brake mode for safety. In the default, coast mode, + // the robot takes very much longer to stop if the joystick is released or + // and emergency stop occurs. In this mode, it stops very quickly. + m_leftFrontMotor.setNeutralMode(NeutralModeValue.Brake); + m_leftBackMotor.setNeutralMode(NeutralModeValue.Brake); + m_rightFrontMotor.setNeutralMode(NeutralModeValue.Brake); + m_rightBackMotor.setNeutralMode(NeutralModeValue.Brake); + /* Set back motors to follow the front motors. */ m_leftBackMotor.setControl(new Follower(m_leftFrontMotor.getDeviceID(), false)); m_rightBackMotor.setControl(new Follower(m_rightFrontMotor.getDeviceID(), false)); m_Drivetrain = new DifferentialDrive(m_leftFrontMotor, m_rightFrontMotor); - - - // Set gyro 0 angle to point forward. - m_NavX.reset(); - m_NavX.zeroYaw(); } - public void initDefaultCommand(Joystick leftJoystick, Joystick rightJoystick, Boolean bArcadeDrive) + public void initDefaultCommand(Joystick leftJoystick) { - if (bArcadeDrive) - { - setDefaultCommand(new ArcadeDriveCommand(this, leftJoystick)); - } - else - { - setDefaultCommand(new TankDriveCommand(this, leftJoystick, rightJoystick)); - } + setDefaultCommand(new ArcadeDriveCommand(this, leftJoystick)); } // Sets left and right motors to set speeds to support tank drive models. @@ -118,11 +111,6 @@ public double getSpeed(boolean bLeft) } } - public double getHeading() - { - return (double)m_NavX.getYaw(); - } - @Override public void periodic() { // This method will be called once per scheduler run diff --git a/2024-Crescendo/src/main/java/frc/robot/subsystems/GooseRotationSubsystem.java b/2024-Crescendo/src/main/java/frc/robot/subsystems/GooseRotationSubsystem.java index cef95e2..79cf7b4 100644 --- a/2024-Crescendo/src/main/java/frc/robot/subsystems/GooseRotationSubsystem.java +++ b/2024-Crescendo/src/main/java/frc/robot/subsystems/GooseRotationSubsystem.java @@ -69,6 +69,14 @@ public GooseRotationSubsystem() m_pidController.setFF(m_kFF); m_pidController.setOutputRange(m_kMinOutput, m_kMaxOutput); + if (GooseRotationConstants.kUseSmartMotion) + { + m_pidController.setSmartMotionMaxVelocity(GooseRotationConstants.kMaxVelocity, GooseRotationConstants.kSmartMotionSlot); + m_pidController.setSmartMotionMinOutputVelocity(GooseRotationConstants.kMinVelocity, GooseRotationConstants.kSmartMotionSlot); + m_pidController.setSmartMotionMaxAccel(GooseRotationConstants.kMaxAcceleration, GooseRotationConstants.kSmartMotionSlot); + m_pidController.setSmartMotionAllowedClosedLoopError(GooseRotationConstants.kMaxError, GooseRotationConstants.kSmartMotionSlot); + } + // display PID coefficients on SmartDashboard SmartDashboard.putNumber("P Gain", m_kP); SmartDashboard.putNumber("I Gain", m_kI); @@ -104,7 +112,14 @@ public void setSetPointAngle(double angleDegrees) double setpoint = getMotorPositionFromAngle(angleDegrees); SmartDashboard.putNumber("Set Degress", m_angleSet); SmartDashboard.putNumber("Set Reference", setpoint); - m_pidController.setReference(setpoint, CANSparkBase.ControlType.kPosition); + if(GooseRotationConstants.kUseSmartMotion) + { + m_pidController.setReference(setpoint, CANSparkBase.ControlType.kSmartMotion); + } + else + { + m_pidController.setReference(setpoint, CANSparkBase.ControlType.kPosition); + } } public double getSetPointAngle() diff --git a/2025-Reefscape/.gradle/8.11/checksums/checksums.lock b/2025-Reefscape/.gradle/8.11/checksums/checksums.lock new file mode 100644 index 0000000..ee330e1 Binary files /dev/null and b/2025-Reefscape/.gradle/8.11/checksums/checksums.lock differ diff --git a/2025-Reefscape/.gradle/8.11/checksums/sha1-checksums.bin b/2025-Reefscape/.gradle/8.11/checksums/sha1-checksums.bin new file mode 100644 index 0000000..d2803bc Binary files /dev/null and b/2025-Reefscape/.gradle/8.11/checksums/sha1-checksums.bin differ diff --git a/2025-Reefscape/.gradle/8.11/fileChanges/last-build.bin b/2025-Reefscape/.gradle/8.11/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/2025-Reefscape/.gradle/8.11/fileChanges/last-build.bin differ diff --git a/2025-Reefscape/.gradle/8.11/fileHashes/fileHashes.lock b/2025-Reefscape/.gradle/8.11/fileHashes/fileHashes.lock new file mode 100644 index 0000000..6e1a0f5 Binary files /dev/null and b/2025-Reefscape/.gradle/8.11/fileHashes/fileHashes.lock differ diff --git a/2025-Reefscape/.gradle/8.11/gc.properties b/2025-Reefscape/.gradle/8.11/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/2025-Reefscape/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/2025-Reefscape/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..786922f Binary files /dev/null and b/2025-Reefscape/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/2025-Reefscape/.gradle/buildOutputCleanup/cache.properties b/2025-Reefscape/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..815d0fe --- /dev/null +++ b/2025-Reefscape/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Jan 13 18:35:10 CST 2026 +gradle.version=8.11 diff --git a/2025-Reefscape/.gradle/vcs-1/gc.properties b/2025-Reefscape/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/2025-Reefscape/Examples/PrototypeBoard/.gitignore b/2025-Reefscape/Examples/PrototypeBoard/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/2025-Reefscape/Examples/PrototypeBoard/.vscode/launch.json b/2025-Reefscape/Examples/PrototypeBoard/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/2025-Reefscape/Examples/PrototypeBoard/.vscode/settings.json b/2025-Reefscape/Examples/PrototypeBoard/.vscode/settings.json new file mode 100644 index 0000000..612cdd0 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] +} diff --git a/2025-Reefscape/Examples/PrototypeBoard/.wpilib/wpilib_preferences.json b/2025-Reefscape/Examples/PrototypeBoard/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a792267 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2025", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/2025-Reefscape/Examples/PrototypeBoard/WPILib-License.md b/2025-Reefscape/Examples/PrototypeBoard/WPILib-License.md new file mode 100644 index 0000000..645e542 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2024 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/2025-Reefscape/Examples/PrototypeBoard/build.gradle b/2025-Reefscape/Examples/PrototypeBoard/build.gradle new file mode 100644 index 0000000..c6fea4f --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/build.gradle @@ -0,0 +1,104 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2025.2.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +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 + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from sourceSets.main.allSource + 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/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.jar b/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.jar differ diff --git a/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.properties b/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2025-Reefscape/Examples/PrototypeBoard/gradlew b/2025-Reefscape/Examples/PrototypeBoard/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/2025-Reefscape/Examples/PrototypeBoard/gradlew.bat b/2025-Reefscape/Examples/PrototypeBoard/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/2025-Reefscape/Examples/PrototypeBoard/settings.gradle b/2025-Reefscape/Examples/PrototypeBoard/settings.gradle new file mode 100644 index 0000000..c493958 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2025' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/2025-Reefscape/Examples/PrototypeBoard/src/main/deploy/example.txt b/2025-Reefscape/Examples/PrototypeBoard/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Main.java b/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Robot.java b/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..c1714ad --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/src/main/java/frc/robot/Robot.java @@ -0,0 +1,113 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.Compressor; +import edu.wpi.first.wpilibj.Solenoid; +import edu.wpi.first.wpilibj.Joystick; +import edu.wpi.first.wpilibj.PneumaticsModuleType; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; +import edu.wpi.first.wpilibj.shuffleboard.ShuffleboardTab; +import com.revrobotics.spark.SparkMax; +import com.revrobotics.spark.SparkLowLevel.MotorType; + +/** + * This is a sample program showing the use of the solenoid classes during operator control. Three + * buttons from a joystick will be used to control two solenoids: One button to control the position + * of a single solenoid and the other two buttons to control a double solenoid. Single solenoids can + * either be on or off, such that the air diverted through them goes through either one channel or + * the other. Double solenoids have three states: Off, Forward, and Reverse. Forward and Reverse + * divert the air through the two channels and correspond to the on and off of a single solenoid, + * but a double solenoid can also be "off", where the solenoid will remain in its default power off + * state. Additionally, double solenoids take up two channels on your PCM whereas single solenoids + * only take a single channel. + */ +public class Robot extends TimedRobot { + private final Joystick m_stick = new Joystick(0); + + // DoubleSolenoid corresponds to a double solenoid. + // In this case, it's connected to channels 1 and 2 of a PH with the default CAN ID. + private final Solenoid m_Solenoid = + new Solenoid(PneumaticsModuleType.REVPH, 0); + + // Compressor connected to a PH with a default CAN ID (1) + private final Compressor m_compressor = new Compressor(PneumaticsModuleType.REVPH); + + static final int kSolenoidForwardButton = 2; + static final int kSolenoidReverseButton = 3; + static final int kCompressorButton = 4; + + static final int kMotor1CANID = 10; + static final int kMotor2CANID = 20; + + private final SparkMax m_Motor1 = new SparkMax(kMotor1CANID, MotorType.kBrushless); + private final SparkMax m_Motor2 = new SparkMax(kMotor2CANID, MotorType.kBrushless); + + /** Called once at the beginning of the robot program. */ + public Robot() { + // Publish elements to shuffleboard. + ShuffleboardTab tab = Shuffleboard.getTab("Pneumatics"); + tab.add("Solenoid", m_Solenoid); + tab.add("Compressor", m_compressor); + + // Also publish some raw data + // Get the pressure (in PSI) from the analog sensor connected to the PH. + // This function is supported only on the PH! + // On a PCM, this function will return 0. + tab.addDouble("PH Pressure [PSI]", m_compressor::getPressure); + // Get compressor current draw. + tab.addDouble("Compressor Current", m_compressor::getCurrent); + // Get whether the compressor is active. + tab.addBoolean("Compressor Active", m_compressor::isEnabled); + // Get the digital pressure switch connected to the PCM/PH. + // The switch is open when the pressure is over ~120 PSI. + tab.addBoolean("Pressure Switch", m_compressor::getPressureSwitchValue); + + m_Motor1.set(0.0); + m_Motor2.set(0.0); + } + + @SuppressWarnings("PMD.UnconditionalIfStatement") + @Override + public void teleopPeriodic() { + + /* + * The joystick Y axis control the speed of motor 1 and the X + * axis controls motor 2. + */ + double speed = m_stick.getY(); + m_Motor1.set(speed); + + speed = m_stick.getX(); + m_Motor2.set(speed); + + /* + * GetRawButtonPressed will only return true once per press. + * If a button is pressed, set the solenoid to the respective channel. + */ + if (m_stick.getRawButtonPressed(kSolenoidForwardButton)) { + m_Solenoid.set(true); + } else if (m_stick.getRawButtonPressed(kSolenoidReverseButton)) { + m_Solenoid.set(false); + } + + // On button press, toggle the compressor. + if (m_stick.getRawButtonPressed(kCompressorButton)) { + // Check whether the compressor is currently enabled. + boolean isCompressorEnabled = m_compressor.isEnabled(); + if (isCompressorEnabled) { + // Disable closed-loop mode on the compressor. + m_compressor.disable(); + } else { + // Enable closed-loop mode based on the analog pressure sensor connected to the PH. + // The compressor will run while the pressure reported by the sensor is in the + // specified range ([70 PSI, 120 PSI] in this example). + // Analog mode exists only on the PH! On the PCM, this enables digital control. + m_compressor.enableAnalog(70, 120); + } + } + } +} diff --git a/2025-Reefscape/vendordeps/REVLib-2025.0.0.json b/2025-Reefscape/Examples/PrototypeBoard/vendordeps/REVLib.json similarity index 90% rename from 2025-Reefscape/vendordeps/REVLib-2025.0.0.json rename to 2025-Reefscape/Examples/PrototypeBoard/vendordeps/REVLib.json index b4f198f..ac62be8 100644 --- a/2025-Reefscape/vendordeps/REVLib-2025.0.0.json +++ b/2025-Reefscape/Examples/PrototypeBoard/vendordeps/REVLib.json @@ -1,7 +1,7 @@ { - "fileName": "REVLib-2025.0.0.json", + "fileName": "REVLib.json", "name": "REVLib", - "version": "2025.0.2", + "version": "2025.0.3", "frcYear": "2025", "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", "mavenUrls": [ @@ -12,14 +12,14 @@ { "groupId": "com.revrobotics.frc", "artifactId": "REVLib-java", - "version": "2025.0.2" + "version": "2025.0.3" } ], "jniDependencies": [ { "groupId": "com.revrobotics.frc", "artifactId": "REVLib-driver", - "version": "2025.0.2", + "version": "2025.0.3", "skipInvalidPlatforms": true, "isJar": false, "validPlatforms": [ @@ -36,7 +36,7 @@ { "groupId": "com.revrobotics.frc", "artifactId": "REVLib-cpp", - "version": "2025.0.2", + "version": "2025.0.3", "libName": "REVLib", "headerClassifier": "headers", "sharedLibrary": false, @@ -53,7 +53,7 @@ { "groupId": "com.revrobotics.frc", "artifactId": "REVLib-driver", - "version": "2025.0.2", + "version": "2025.0.3", "libName": "REVLibDriver", "headerClassifier": "headers", "sharedLibrary": false, diff --git a/2025-Reefscape/Examples/PrototypeBoard/vendordeps/WPILibNewCommands.json b/2025-Reefscape/Examples/PrototypeBoard/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..3718e0a --- /dev/null +++ b/2025-Reefscape/Examples/PrototypeBoard/vendordeps/WPILibNewCommands.json @@ -0,0 +1,38 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", + "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" + ] + } + ] +} diff --git a/2025-Reefscape/Examples/Shooter/.gitignore b/2025-Reefscape/Examples/Shooter/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/2025-Reefscape/Examples/Shooter/.vscode/launch.json b/2025-Reefscape/Examples/Shooter/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/2025-Reefscape/Examples/Shooter/.vscode/settings.json b/2025-Reefscape/Examples/Shooter/.vscode/settings.json new file mode 100644 index 0000000..612cdd0 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] +} diff --git a/2025-Reefscape/Examples/Shooter/.wpilib/wpilib_preferences.json b/2025-Reefscape/Examples/Shooter/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a792267 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2025", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/2025-Reefscape/Examples/Shooter/WPILib-License.md b/2025-Reefscape/Examples/Shooter/WPILib-License.md new file mode 100644 index 0000000..645e542 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2024 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/2025-Reefscape/Examples/Shooter/build.gradle b/2025-Reefscape/Examples/Shooter/build.gradle new file mode 100644 index 0000000..ff9e9af --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/build.gradle @@ -0,0 +1,104 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2025.3.2" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +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 + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from sourceSets.main.allSource + 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/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.jar b/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.jar differ diff --git a/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.properties b/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2025-Reefscape/Examples/Shooter/gradlew b/2025-Reefscape/Examples/Shooter/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/2025-Reefscape/Examples/Shooter/gradlew.bat b/2025-Reefscape/Examples/Shooter/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/2025-Reefscape/Examples/Shooter/settings.gradle b/2025-Reefscape/Examples/Shooter/settings.gradle new file mode 100644 index 0000000..969c7b0 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2025' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name 'frcHome' + url frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/2025-Reefscape/Examples/Shooter/src/main/deploy/example.txt b/2025-Reefscape/Examples/Shooter/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Main.java b/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Robot.java b/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..c2559b5 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/src/main/java/frc/robot/Robot.java @@ -0,0 +1,58 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import com.revrobotics.spark.SparkMax; +import com.revrobotics.spark.SparkLowLevel.MotorType; +/** + * This sample program shows how to control a motor using a joystick. In the operator control part + * of the program, the joystick is read and the value is written to the motor. + * + *

Joystick analog values range from -1 to 1 and motor controller inputs also range from -1 to 1 + * making it easy to work together. + * + *

In addition, the encoder value of an encoder connected to ports 0 and 1 is consistently sent + * to the Dashboard. + */ +public class Robot extends TimedRobot { + private static final int kMotorPort = 50; // CAN ID + private static final int kJoystickPort = 0; + //private static final int kEncoderPortA = 0; + //private static final int kEncoderPortB = 1; + + private final SparkMax m_motor; + private final XboxController m_joystick; + //private final Encoder m_encoder; + + /** Called once at the beginning of the robot program. */ + public Robot() { + m_motor = new SparkMax(kMotorPort, MotorType.kBrushless); + m_joystick = new XboxController(kJoystickPort); + // m_encoder = new Encoder(kEncoderPortA, kEncoderPortB); + // Use SetDistancePerPulse to set the multiplier for GetDistance + // This is set up assuming a 6 inch wheel with a 360 CPR encoder. + // m_encoder.setDistancePerPulse((Math.PI * 6) / 360.0); + } + + /* + * The RobotPeriodic function is called every control packet no matter the + * robot mode. + */ + @Override + public void robotPeriodic() { + // SmartDashboard.putNumber("Encoder", m_encoder.getDistance()); + } + + /** The teleop periodic function is called every control packet in teleop. */ + @Override + public void teleopPeriodic() { + double speed = m_joystick.getLeftY(); + m_motor.set(speed); + } +} diff --git a/2025-Reefscape/Examples/Shooter/vendordeps/REVLib.json b/2025-Reefscape/Examples/Shooter/vendordeps/REVLib.json new file mode 100644 index 0000000..ac62be8 --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/vendordeps/REVLib.json @@ -0,0 +1,71 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2025.0.3", + "frcYear": "2025", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2025.0.3" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2025.0.3", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/2025-Reefscape/Examples/Shooter/vendordeps/WPILibNewCommands.json b/2025-Reefscape/Examples/Shooter/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..3718e0a --- /dev/null +++ b/2025-Reefscape/Examples/Shooter/vendordeps/WPILibNewCommands.json @@ -0,0 +1,38 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", + "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" + ] + } + ] +} diff --git a/2025-Reefscape/build.gradle b/2025-Reefscape/build.gradle index 1945af5..44f1061 100644 --- a/2025-Reefscape/build.gradle +++ b/2025-Reefscape/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2025.2.1" + id "edu.wpi.first.GradleRIO" version "2025.3.2" } java { diff --git a/2025-Reefscape/src/main/java/frc/robot/Constants.java b/2025-Reefscape/src/main/java/frc/robot/Constants.java index f9a741c..92e0860 100644 --- a/2025-Reefscape/src/main/java/frc/robot/Constants.java +++ b/2025-Reefscape/src/main/java/frc/robot/Constants.java @@ -69,10 +69,14 @@ public static class OperatorConstants { public static final int kIntakeElevatorPosition = 2; // Button B public static final int kIncreaseElevatorLevel = 4; // Button Y + // In the future when disabling buttons, please comment out the command bind + // under the configureBindings function in the RobotContrainer. - Owen public static final int kCoralIntake = 6; // Right Bumper public static final int kCoralOuttake = 5; // Left Bumper public static final int kCoralStop = 9; // Left Thumbstick Button + public static final int kShooterShoot = 5; // Left Bumper + public static final int kAlageIntake = 8; // START public static final int kAlageOutput = 7; // BACK @@ -147,6 +151,25 @@ public static class CoralConstants { public static final int kTicksPerUpdate = 10; } + public static class ShooterConstants { + public static final int kIntakeMotorCANID = 40; + public static final int kOutputMotorCANID = 41; + + public static final double kMotorIntakeSpeed = 1.0; + public static final double kMotorOutputSpeed = -1.0; + + // number of ticks between sensor change and motor stop + public static final int kEndIntakeMaxCount = 6; // ~0.125 second(s) + public static final int kEndOutputMaxCount = 50; // 1 second(s) + + // Line Break? sensor to detect coral in the middle + public static final int kSensorChannel = 0; + public static final boolean kSensorFalseIsEmpty = false; + + // SmartDashboard update frequency for coral subsystem state in 20ms counts. + public static final int kTicksPerUpdate = 10; + } + public static class AlgaeConstants { public static final int kMotorCANID = 50; public static final int kMotorCurrentLimit = 7; @@ -159,7 +182,7 @@ public static class AlgaeConstants { public static final boolean kSensorFalseIsEmpty = true; // number of ticks between sensor change and motor stop - public static final int kEndIntakeMaxCount = 0; // 0 second(s) + public static final int kEndIntakeMaxCount = 15; // 0 second(s) public static final int kEndOutputMaxCount = 100; // 2 second(s) public static final double kIntakeSpeed = 1; @@ -188,8 +211,8 @@ public static class ElevatorConstants { public static final double kElevatorIntakePosition = 0.0; public static final double kElevatorL2Position = 0.46; // 0.45-0.47?? - public static final double kElevatorL3Position = 0.82; - public static final double kElevatorL4Position = 1.51; + public static final double kElevatorL3Position = 0.93; + public static final double kElevatorL4Position = 1.53; // The distance travelled for a single rotation of the Kraken output shaft. // 64 to 1 gear box, pulley is 47.75mm diameter (0.15m circumference), @@ -199,9 +222,4 @@ public static class ElevatorConstants { // SmartDashboard update frequency for elevator subsystem state in 20ms counts. public static final int kTicksPerUpdate = 5; } - - public static class DrivetrainConstants { - public static final int kLeftMotorCANID = 10; - public static final int kRightMotorCANID = 20; - } } diff --git a/2025-Reefscape/src/main/java/frc/robot/RobotContainer.java b/2025-Reefscape/src/main/java/frc/robot/RobotContainer.java index ff43b79..3cea0bd 100644 --- a/2025-Reefscape/src/main/java/frc/robot/RobotContainer.java +++ b/2025-Reefscape/src/main/java/frc/robot/RobotContainer.java @@ -25,6 +25,7 @@ import frc.robot.subsystems.DriveSubsystem; import frc.robot.subsystems.ElevatorSubsystem; import frc.robot.subsystems.IntakeSubsystem; +import frc.robot.subsystems.ShooterSubsystem; import java.util.Optional; @@ -54,6 +55,7 @@ public class RobotContainer { private final DriveSubsystem m_driveSubsystem = new DriveSubsystem(); private final Optional m_intakeSubsystem; private final Optional m_coralSubsystem; + private final Optional m_shooterSubsystem; private final Optional m_elevatorSubsystem; private final Optional m_algaeSubsystem; @@ -88,6 +90,10 @@ public class RobotContainer { private final JoystickButton m_coralStopMotorsButton = new JoystickButton(m_operatorController, OperatorConstants.kCoralStop); + private final JoystickButton m_shooterButton = + new JoystickButton(m_operatorController, OperatorConstants.kShooterShoot); + + private ElevatorManualSpeed m_manualSpeedCommand; // Keep track of time for SmartDashboard updates. @@ -99,6 +105,7 @@ public RobotContainer() { m_intakeSubsystem = getSubsystem(IntakeSubsystem.class); m_coralSubsystem = getSubsystem(CoralSubsystem.class); + m_shooterSubsystem = getSubsystem(ShooterSubsystem.class); m_elevatorSubsystem = getSubsystem(ElevatorSubsystem.class); m_algaeSubsystem = getSubsystem(AlgaeSubsystem.class); @@ -198,6 +205,7 @@ private void configureBindings() { SmartDashboard.putBoolean("Intake Subsystem", m_intakeSubsystem.isPresent()); SmartDashboard.putBoolean("Coral Subsystem", m_coralSubsystem.isPresent()); + SmartDashboard.putBoolean("Shooter Subsystem", m_shooterSubsystem.isPresent()); SmartDashboard.putBoolean("Elevator Subsystem", m_elevatorSubsystem.isPresent()); SmartDashboard.putBoolean("Algae Subsystem", m_algaeSubsystem.isPresent()); @@ -209,6 +217,12 @@ private void configureBindings() { // TODO: Bind intake commands and controls. } + if (m_shooterSubsystem.isPresent()) + { + ShooterSubsystem shooterSubsystem = m_shooterSubsystem.get(); + // Poopoo + } + if (m_coralSubsystem.isPresent()) { CoralSubsystem coralSubsystem = m_coralSubsystem.get(); diff --git a/2025-Reefscape/src/main/java/frc/robot/commands/ShooterSpeedCommand.java b/2025-Reefscape/src/main/java/frc/robot/commands/ShooterSpeedCommand.java new file mode 100644 index 0000000..7547399 --- /dev/null +++ b/2025-Reefscape/src/main/java/frc/robot/commands/ShooterSpeedCommand.java @@ -0,0 +1,55 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.ShooterSubsystem; + +// TODO: Class header comment! + +/** An example command that uses an example subsystem. */ +public class ShooterSpeedCommand extends Command { + private final ShooterSubsystem m_subsystem; + private double m_intakeSpeed = 0.0; + private double m_outputSpeed = 0.0; + + /** + * Creates a new ShooterSpeedCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ShooterSpeedCommand(ShooterSubsystem subsystem, double inputSpeed, double outputSpped) + { + m_subsystem = subsystem; + + m_intakeSpeed = inputSpeed; + m_outputSpeed = outputSpped; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.setIntakeSpeed(m_intakeSpeed); + m_subsystem.setOutputSpeed(m_outputSpeed); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + m_subsystem.setIntakeSpeed(0); + m_subsystem.setOutputSpeed(0); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/2025-Reefscape/src/main/java/frc/robot/subsystems/ShooterSubsystem.java b/2025-Reefscape/src/main/java/frc/robot/subsystems/ShooterSubsystem.java new file mode 100644 index 0000000..e28016f --- /dev/null +++ b/2025-Reefscape/src/main/java/frc/robot/subsystems/ShooterSubsystem.java @@ -0,0 +1,57 @@ +// 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 frc.robot.subsystems; + +import com.revrobotics.spark.SparkLowLevel.MotorType; +import com.revrobotics.spark.SparkMax; + +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.Constants.ShooterConstants; + +public class ShooterSubsystem extends SubsystemBase { + private double m_intakeSpeed = 0; + private double m_outputSpeed = 0; + + private final SparkMax m_intakeMotor = new SparkMax(ShooterConstants.kIntakeMotorCANID, + MotorType.kBrushless); + private final SparkMax m_outputMotor = new SparkMax(ShooterConstants.kOutputMotorCANID, + MotorType.kBrushless); + + /** Creates a new ShooterSubsystem. */ + public ShooterSubsystem() { + // We need to know if the motor controllers we need are + // actually present on the CAN bus and, unfortunately, their + // constructors don't seem to throw exceptions in this case. Let's + // query the firmware fault status and use this for now. + if (m_intakeMotor.getFaults().firmware || m_outputMotor.getFaults().firmware) + { + throw new RuntimeException("Shooter subsystem motors not present"); + } + } + + public void setIntakeSpeed(double speed) + { + m_intakeMotor.set(speed); + m_intakeSpeed = speed; + } + + // Returns the last COMMANDED speed + public double getIntakeSpeed() + { + return m_intakeSpeed; + } + + public void setOutputSpeed(double speed) + { + m_outputMotor.set(speed); + m_outputSpeed = speed; + } + + // Returns the last COMMANDED speed. + public double getOutputSpeed() + { + return m_outputSpeed; + } +} diff --git a/2025-Reefscape/vendordeps/Phoenix6-25.3.1.json b/2025-Reefscape/vendordeps/Phoenix6-frc2025-latest.json similarity index 72% rename from 2025-Reefscape/vendordeps/Phoenix6-25.3.1.json rename to 2025-Reefscape/vendordeps/Phoenix6-frc2025-latest.json index 2b0bfd1..6f40c84 100644 --- a/2025-Reefscape/vendordeps/Phoenix6-25.3.1.json +++ b/2025-Reefscape/vendordeps/Phoenix6-frc2025-latest.json @@ -1,13 +1,7 @@ { -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "fileName": "Phoenix6-25.3.1.json", + "fileName": "Phoenix6-frc2025-latest.json", "name": "CTRE-Phoenix (v6)", - "version": "25.3.1", -======== - "fileName": "Phoenix6-25.1.0.json", - "name": "CTRE-Phoenix (v6)", - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "frcYear": "2025", "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", "mavenUrls": [ @@ -25,22 +19,14 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-java", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1" -======== - "version": "25.1.0" ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0" } ], "jniDependencies": [ { "groupId": "com.ctre.phoenix6", "artifactId": "api-cpp", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -54,11 +40,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -72,11 +54,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "api-cpp-sim", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -90,11 +68,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -108,11 +82,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -126,11 +96,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -144,11 +110,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -162,11 +124,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -180,18 +138,13 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", "linuxarm64", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json "osxuniversal" ], "simMode": "swsim" @@ -199,15 +152,13 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFXS", - "version": "25.3.1", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ "windowsx86-64", "linuxx86-64", "linuxarm64", -======== ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json "osxuniversal" ], "simMode": "swsim" @@ -215,11 +166,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -233,11 +180,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -251,8 +194,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANrange", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -266,10 +208,21 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANdi", - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", "isJar": false, "skipInvalidPlatforms": true, "validPlatforms": [ @@ -285,11 +238,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "wpiapi-cpp", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_Phoenix6_WPI", "headerClassifier": "headers", "sharedLibrary": true, @@ -305,11 +254,7 @@ { "groupId": "com.ctre.phoenix6", "artifactId": "tools", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_PhoenixTools", "headerClassifier": "headers", "sharedLibrary": true, @@ -325,11 +270,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "wpiapi-cpp-sim", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_Phoenix6_WPISim", "headerClassifier": "headers", "sharedLibrary": true, @@ -345,11 +286,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "tools-sim", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_PhoenixTools_Sim", "headerClassifier": "headers", "sharedLibrary": true, @@ -365,11 +302,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simTalonSRX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimTalonSRX", "headerClassifier": "headers", "sharedLibrary": true, @@ -385,11 +318,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simVictorSPX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimVictorSPX", "headerClassifier": "headers", "sharedLibrary": true, @@ -405,11 +334,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simPigeonIMU", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimPigeonIMU", "headerClassifier": "headers", "sharedLibrary": true, @@ -425,11 +350,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simCANCoder", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimCANCoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -445,11 +366,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFX", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimProTalonFX", "headerClassifier": "headers", "sharedLibrary": true, @@ -458,7 +375,6 @@ "windowsx86-64", "linuxx86-64", "linuxarm64", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json "osxuniversal" ], "simMode": "swsim" @@ -466,7 +382,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProTalonFXS", - "version": "25.3.1", + "version": "25.4.0", "libName": "CTRE_SimProTalonFXS", "headerClassifier": "headers", "sharedLibrary": true, @@ -475,8 +391,6 @@ "windowsx86-64", "linuxx86-64", "linuxarm64", -======== ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json "osxuniversal" ], "simMode": "swsim" @@ -484,11 +398,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANcoder", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimProCANcoder", "headerClassifier": "headers", "sharedLibrary": true, @@ -504,11 +414,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProPigeon2", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimProPigeon2", "headerClassifier": "headers", "sharedLibrary": true, @@ -524,11 +430,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANrange", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json - "version": "25.3.1", -======== - "version": "25.1.0", ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "version": "25.4.0", "libName": "CTRE_SimProCANrange", "headerClassifier": "headers", "sharedLibrary": true, @@ -537,7 +439,6 @@ "windowsx86-64", "linuxx86-64", "linuxarm64", -<<<<<<<< HEAD:vendordeps/Phoenix6-25.3.1.json "osxuniversal" ], "simMode": "swsim" @@ -545,7 +446,7 @@ { "groupId": "com.ctre.phoenix6.sim", "artifactId": "simProCANdi", - "version": "25.3.1", + "version": "25.4.0", "libName": "CTRE_SimProCANdi", "headerClassifier": "headers", "sharedLibrary": true, @@ -554,8 +455,22 @@ "windowsx86-64", "linuxx86-64", "linuxarm64", -======== ->>>>>>>> main:vendordeps/Phoenix6-25.1.0.json + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", "osxuniversal" ], "simMode": "swsim" diff --git a/2025-Reefscape/vendordeps/REVLib.json b/2025-Reefscape/vendordeps/REVLib.json new file mode 100644 index 0000000..ac62be8 --- /dev/null +++ b/2025-Reefscape/vendordeps/REVLib.json @@ -0,0 +1,71 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2025.0.3", + "frcYear": "2025", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2025.0.3" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2025.0.3", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/2025-Reefscape/vendordeps/libgrapplefrc2025.json b/2025-Reefscape/vendordeps/libgrapplefrc2025.json index f593245..a49af31 100644 --- a/2025-Reefscape/vendordeps/libgrapplefrc2025.json +++ b/2025-Reefscape/vendordeps/libgrapplefrc2025.json @@ -1,7 +1,7 @@ { "fileName": "libgrapplefrc2025.json", "name": "libgrapplefrc", - "version": "2025.1.0", + "version": "2025.1.3", "frcYear": "2025", "uuid": "8ef3423d-9532-4665-8339-206dae1d7168", "mavenUrls": [ @@ -12,14 +12,14 @@ { "groupId": "au.grapplerobotics", "artifactId": "libgrapplefrcjava", - "version": "2025.1.0" + "version": "2025.1.3" } ], "jniDependencies": [ { "groupId": "au.grapplerobotics", "artifactId": "libgrapplefrcdriver", - "version": "2025.1.0", + "version": "2025.1.3", "skipInvalidPlatforms": true, "isJar": false, "validPlatforms": [ @@ -37,7 +37,7 @@ { "groupId": "au.grapplerobotics", "artifactId": "libgrapplefrccpp", - "version": "2025.1.0", + "version": "2025.1.3", "libName": "grapplefrc", "headerClassifier": "headers", "sharedLibrary": true, @@ -55,7 +55,7 @@ { "groupId": "au.grapplerobotics", "artifactId": "libgrapplefrcdriver", - "version": "2025.1.0", + "version": "2025.1.3", "libName": "grapplefrcdriver", "headerClassifier": "headers", "sharedLibrary": true, diff --git a/2026-Rebuilt/.vscode/settings.json b/2026-Rebuilt/.vscode/settings.json index 5e6ede8..b20429d 100644 --- a/2026-Rebuilt/.vscode/settings.json +++ b/2026-Rebuilt/.vscode/settings.json @@ -57,5 +57,6 @@ "edu.wpi.first.math.**.proto.*", "edu.wpi.first.math.**.struct.*", ], - "java.dependency.enableDependencyCheckup": false + "java.dependency.enableDependencyCheckup": false, + "search.useIgnoreFiles": true } diff --git a/2026-Rebuilt/DriveForwardFromPosMath.png b/2026-Rebuilt/DriveForwardFromPosMath.png new file mode 100644 index 0000000..82be272 Binary files /dev/null and b/2026-Rebuilt/DriveForwardFromPosMath.png differ diff --git a/2026-Rebuilt/Modified2026Andymark.fmap b/2026-Rebuilt/Modified2026Andymark.fmap new file mode 100644 index 0000000..0afcca2 --- /dev/null +++ b/2026-Rebuilt/Modified2026Andymark.fmap @@ -0,0 +1 @@ +{"fiducials":[{"family":"apriltag3_36h11_classic","id":1,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,3.604958999999999,1.2246467991473532e-16,-1,0,3.3899913999999995,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":2,"size":165.1,"transform":[-2.220446049250313e-16,-1.0000000000000002,0,3.6423986,1.0000000000000002,-2.220446049250313e-16,0,0.6032558000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":3,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,3.0388438000000004,1.2246467991473532e-16,-1,0,0.35545340000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":4,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,3.0388438000000004,1.2246467991473532e-16,-1,0,-0.0001465999999998857,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":5,"size":165.1,"transform":[-2.220446049250313e-16,1,0,3.6423986,-1,-2.220446049250313e-16,0,-0.6035489999999997,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":6,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,3.604958999999999,1.2246467991473532e-16,-1,0,-3.3902845999999998,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":7,"size":165.1,"transform":[1,0,0,3.679863599999999,0,1,0,-3.3902845999999998,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":8,"size":165.1,"transform":[-2.220446049250313e-16,1,0,3.997998599999999,-1,-2.220446049250313e-16,0,-0.6035489999999997,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":9,"size":165.1,"transform":[1,0,0,4.246156599999999,0,1,0,-0.3557465999999998,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":10,"size":165.1,"transform":[1,0,0,4.246156599999999,0,1,0,-0.0001465999999998857,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":11,"size":165.1,"transform":[-2.220446049250313e-16,-1.0000000000000002,0,3.997998599999999,1.0000000000000002,-2.220446049250313e-16,0,0.6032558000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":12,"size":165.1,"transform":[1,0,0,3.679863599999999,0,1,0,3.3899913999999995,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":13,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,8.240331999999999,1.2246467991473532e-16,-1,0,3.3704079999999994,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":14,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,8.240331999999999,1.2246467991473532e-16,-1,0,2.9386079999999994,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":15,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,8.2399764,1.2246467991473532e-16,-1,0,0.29098820000000014,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":16,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,8.2399764,1.2246467991473532e-16,-1,0,-0.14081180000000026,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":17,"size":165.1,"transform":[1,0,0,-3.6099364000000005,0,1,0,-3.3902845999999998,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":18,"size":165.1,"transform":[-2.220446049250313e-16,1,0,-3.6474014000000006,-1,-2.220446049250313e-16,0,-0.6035489999999997,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":19,"size":165.1,"transform":[1,0,0,-3.0438466,0,1,0,-0.3557465999999998,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":20,"size":165.1,"transform":[1,0,0,-3.0438466,0,1,0,-0.0001465999999998857,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":21,"size":165.1,"transform":[-2.220446049250313e-16,-1.0000000000000002,0,-3.6474014000000006,1.0000000000000002,-2.220446049250313e-16,0,0.6032558000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":22,"size":165.1,"transform":[1,0,0,-3.6099364000000005,0,1,0,3.3899913999999995,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":23,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,-3.6848410000000005,1.2246467991473532e-16,-1,0,3.3899913999999995,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":24,"size":165.1,"transform":[-2.220446049250313e-16,-1.0000000000000002,0,-4.0030014000000005,1.0000000000000002,-2.220446049250313e-16,0,0.6032558000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":25,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,-4.251134,1.2246467991473532e-16,-1,0,0.35545340000000003,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":26,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,-4.251134,1.2246467991473532e-16,-1,0,-0.0001465999999998857,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":27,"size":165.1,"transform":[-2.220446049250313e-16,1,0,-4.0030014000000005,-1,-2.220446049250313e-16,0,-0.6035489999999997,0,0,1,1.12395,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":28,"size":165.1,"transform":[-1,-1.2246467991473532e-16,0,-3.6848410000000005,1.2246467991473532e-16,-1,0,-3.3902845999999998,0,0,1,0.889,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":29,"size":165.1,"transform":[1,0,0,-8.2453094,0,1,0,-3.3707266,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":30,"size":165.1,"transform":[1,0,0,-8.2453094,0,1,0,-2.9389265999999994,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":31,"size":165.1,"transform":[1,0,0,-8.244953800000001,0,1,0,-0.29130679999999964,0,0,1,0.55245,0,0,0,1],"unique":true},{"family":"apriltag3_36h11_classic","id":32,"size":165.1,"transform":[1,0,0,-8.244953800000001,0,1,0,0.14049319999999987,0,0,1,0.55245,0,0,0,1],"unique":true},{"transform":[1,-1.6081226496766364e-16,9.211731732618967e-33,0,1.6081226496766364e-16,1,-1.2325951644078308e-32,2.104153938435746e-16,-9.211731732618965e-33,1.232595164407831e-32,1,0,0,0,0,1],"size":165.1,"id":50,"family":"apriltag3_36h11_classic","unique":1},{"transform":[-1,-1.2246467991473532e-16,3.081487911019576e-33,0.1524,1.2246467991473532e-16,-1,-1.6081226496766364e-16,0.3048,2.2775310466648105e-32,-1.6081226496766364e-16,1,0.375,0,0,0,1],"size":165.1,"id":52,"family":"apriltag3_36h11_classic","unique":1},{"family":"apriltag3_36h11_classic","id":54,"size":165.1,"unique":true,"transform":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},{"family":"apriltag3_36h11_classic","id":56,"size":165.1,"unique":true,"transform":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}],"type":"frc","fieldlength":16.518,"fieldwidth":8.043,"pngBase64":"data:image/png;base64,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Uk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAMAUExURQAAAP///2VlZWZmZmpqamlpaWhoaGdnZwsGCNrV1y4aLp6KojoqPuro6wICtwICsQMD1gMDyQICpQICkQICbAQEvwkJ7QoK9gkJ4wcHrwQEUhQU7BYW9Q0NlxISxxERuBUV1xYWcCIikT4+/Tc33kVF/hcXPV9fiklJamdnk3FxoFxcfJKSvH9/pLS04AwMDRYWFzExMgMEKAIDEyIjMh0eKnJ0gl1falRVWzM1P1laXywwPigrNjk8R0FEThEVIRgbI09RV4SHjwQFB0dMUwkQGHuBiDU4O2ltcZaZm3R9ggUKDIGGiGJqbXJ2d1pfYCAnKAwrKyIxMTRJSS4/P0ZaWltwcFdoaFFgYGp7e2Rzc5uoqIqOjpKWlklKSpainyImIjpuMlKKRlZ2TgYKAgsLBGFhS1VVRWVlVltbT2xsX3R0a2BgWYeHf2ZmYI6OiIqKhW5uanp6dpKSjpaWkv78kE1LOfzqcxMRBoGAehkWBqSVTyklFTEuITYzJyIdC/HRXDMtGEI9KiknIOK9SbqdP6iOOpiCOHxpL414N7CMIJh5IYtvH7GMKaCAJbqULaiHKWtWGn1kH2FOGEE0EMOcMXRdHVZFFpd6J4RrI7COL45zJks9FJ6BK8ukONWuP2NTJlNGIT41GkY8H1pZVi4kDDcsDzs5NBwZE0dANQcEAiUbFSYfGzsyLTMiIb4CArYCArECAqUCApQCAtMEBHcDAzoCAvcJCVEEBOYMDM0NDbwMDB8CAqwMDJ4MDOwUFPwWFhICAq8cHP0qKsIiIuYqKv01Na0lJYseHlgVFSoNDUAYGHhTU4dfX2dJSZFoaFlAQJxycriQkKWBgXdhYd62toBqamVVVVFGRoB6em9qapyWlmpmZnp2doqGhoaCgpaSkrOxscHBwaWlpZubm5aWlpKSko6OjoqKioaGhoKCgn5+fnp6enZ2dnJycm5ubmJiYl5eXlpaWlZWVlJSUk5OTkNDQzs7OzY2NisrKyYmJiIiIhwcHBISEgICAv////17gRMAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAAsTAAALEwEAmpwYAAC6uUlEQVR4nNR9d4AU9dn/55mZnS2z5doeTSkxhpJiN2JMFBEEC5jYYo0NBDVG42Hhou8vMXdGPTQaFQU0JjFFkxiBKIhiefNG7OVNXpAYBRQO7vZuy+1tnfL8/piZLXe7e7tHkTwHd7tTvt/vzDzzPJ/v074kCWAGQtgNCpp/QgWfc9+rOLvouGD+zNv7X0h/4dDbqXgvCVd86+aUS7/rf1aAjYG9MADCQOLBvVLFvYMOLXEM55sxFE1AUqzQUK673DFtuX2Lrb/taLX2LB54cGEr7dYpXOIQAqi9lb9/1ggy5HvvF5nBQMJj77+r8zq165FfCea3pLU9ASWhAEjAA6Dl7oGD7wCAGwZd000AgJ8N2l5MXHivK99q4onnUioQYgANQzRbmcJAYRvm12G2Gc6d94Pkn7MjT1puWF/ZbJgQvPkhDWLn9XcADDQUMSATJGgF1z2YOwEAkma1SQAgFu7Kn0wAaZCgARDBFe5nYpejITGiYMPglwwg8/xB72muVWIQwDSoHy68Fvv9sA9qs9kX7a3mmPmas6gOmUeXisxA2+J8e8b3b4jW/fznVP51NJky/w2AwsRUfFgLgA4M3m7ybgnmzdFNt99akoVvvv3WOwHjSxIMDGx0t6kB4eEzuHWm9VQCA3cTsGA1AEAyNxR3RRIAKc+RVHBkEUkANJC5r+gG5G8HE1g0n53ElfgxBMHsIxREKBgKhoLFu80NA9ixkA/z/aHocdicyNZPfhvbPNXWmuOLxfkTvfm2C/gRDKgCQFzAkcQFY0goRRepgImR9GAA53UAYFp098DtaLH2l2XJO7MjSm2+1xpLr1DifaydGhpQLBB3T+CaRLcBiJXYce3pAAANpeSfKfns7WX50SKpwj4AzNZRlbQxAAYSDABBIIggggP2h4KhkMmJheKR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"} \ No newline at end of file diff --git a/2026-Rebuilt/Notes.txt b/2026-Rebuilt/Notes.txt new file mode 100644 index 0000000..1b43cbe --- /dev/null +++ b/2026-Rebuilt/Notes.txt @@ -0,0 +1,19 @@ +Test 1: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch + +Test 2: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch + +Test 3: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch + + +Pose for distance testing: +X: 1.52 +Y: 4.26 \ No newline at end of file diff --git a/2026-Rebuilt/build.gradle b/2026-Rebuilt/build.gradle index e9b0020..a3a6e39 100644 --- a/2026-Rebuilt/build.gradle +++ b/2026-Rebuilt/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2026.1.1" + id "edu.wpi.first.GradleRIO" version "2026.2.1" } java { @@ -33,7 +33,7 @@ deploy { frcStaticFileDeploy(getArtifactTypeClass('FileTreeArtifact')) { files = project.fileTree('src/main/deploy') directory = '/home/lvuser/deploy' - deleteOldFiles = false // Change to true to delete files on roboRIO that no + deleteOldFiles = true // Change to true to delete files on roboRIO that no // longer exist in deploy directory of this project } } @@ -48,7 +48,7 @@ def deployArtifact = deploy.targets.roborio.artifacts.frcJava 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 5. diff --git a/2026-Rebuilt/elastic-layout.json b/2026-Rebuilt/elastic-layout.json new file mode 100644 index 0000000..d140cf3 --- /dev/null +++ b/2026-Rebuilt/elastic-layout.json @@ -0,0 +1,782 @@ +{ + "version": 1.0, + "grid_size": 32, + "tabs": [ + { + "name": "Toaster Testing", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Drive Commands", + "x": 0.0, + "y": 0.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Drive Commands", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Auto Chooser", + "x": 0.0, + "y": 128.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Auto Chooser", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Field", + "x": 1152.0, + "y": 0.0, + "width": 640.0, + "height": 384.0, + "type": "Field", + "properties": { + "topic": "/SmartDashboard/Field", + "period": 0.06, + "field_game": "Rebuilt", + "robot_width": 0.85, + "robot_length": 0.85, + "show_other_objects": true, + "show_trajectories": true, + "field_rotation": 0.0, + "robot_color": 4294198070, + "trajectory_color": 4294967295, + "show_robot_outside_widget": true + } + }, + { + "title": "Pose X (Meter)", + "x": 1792.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose X (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Y (Meter)", + "x": 1792.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Y (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Theta (Degrees)", + "x": 1792.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Theta (Degrees)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "limelight", + "x": 512.0, + "y": 0.0, + "width": 640.0, + "height": 384.0, + "type": "Camera Stream", + "properties": { + "topic": "/CameraPublisher/limelight", + "period": 0.06, + "rotation_turns": 0 + } + }, + { + "title": "Left Speed (MPS)", + "x": 256.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Right Speed (MPS)", + "x": 384.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Left Target (MPS)", + "x": 256.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Right Target (MPS)", + "x": 384.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Hub Distance", + "x": 384.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Hub Distance", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "mt2 Tag Count", + "x": 256.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/mt2 Tag Count", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Safe To Shoot", + "x": 0.0, + "y": 384.0, + "width": 256.0, + "height": 128.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Launcher SafeToShoot", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Starting Position", + "x": 0.0, + "y": 256.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Starting Position", + "period": 0.06, + "sort_options": false + } + } + ] + } + }, + { + "name": "Pit Test", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Climb Raise", + "x": 448.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/ClimbL1 TestRaise", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Raised", + "x": 448.0, + "y": 192.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/ClimbL1Raised", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Hub Distance", + "x": 0.0, + "y": 192.0, + "width": 192.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Hub Distance", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Running", + "x": 640.0, + "y": 192.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/IndexerBulk BulkStarted", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Bulk Run", + "x": 640.0, + "y": 0.0, + "width": 128.0, + "height": 96.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/IndexerBulk TestBulk", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Intake Extend", + "x": 960.0, + "y": 0.0, + "width": 128.0, + "height": 96.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/Intake TestExtend", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Intake Run", + "x": 1088.0, + "y": 0.0, + "width": 128.0, + "height": 96.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/Intake TestRun", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Running", + "x": 1088.0, + "y": 192.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Intake Running", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Extended", + "x": 960.0, + "y": 192.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Intake Extended", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Launcher RPM", + "x": 0.0, + "y": 96.0, + "width": 192.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher RPM", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Set Launcher RPM", + "x": 0.0, + "y": 0.0, + "width": 192.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestRPM", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Lift Speed", + "x": 256.0, + "y": 96.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Lift TestLiftSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Fuel Present", + "x": 0.0, + "y": 288.0, + "width": 192.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Launcher Has Fuel", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Subsystem", + "x": 448.0, + "y": 352.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/ClimbL1 Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Drive Subsystem", + "x": 0.0, + "y": 448.0, + "width": 192.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Drive Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Subsystem", + "x": 640.0, + "y": 352.0, + "width": 256.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/IndexerBulk Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Subsystem", + "x": 960.0, + "y": 352.0, + "width": 256.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Intake Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Subsystem", + "x": 0.0, + "y": 352.0, + "width": 192.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Launcher Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Speed", + "x": 640.0, + "y": 256.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/IndexerBulk BulkSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Speed", + "x": 768.0, + "y": 256.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/IndexerBulk IndexerSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Indexer Run", + "x": 768.0, + "y": 0.0, + "width": 128.0, + "height": 96.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/IndexerBulk TestIndexer", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Running", + "x": 768.0, + "y": 192.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/IndexerBulk IndexerStarted", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Set Speed", + "x": 640.0, + "y": 96.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/IndexerBulk TestBulkSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Set Speed", + "x": 768.0, + "y": 96.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/IndexerBulk TestIndexerSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Launcher RPM", + "x": 1248.0, + "y": 0.0, + "width": 640.0, + "height": 416.0, + "type": "Graph", + "properties": { + "topic": "/SmartDashboard/Launcher RPM", + "period": 0.033, + "data_type": "double", + "time_displayed": 15.0, + "min_value": 0.0, + "max_value": 4000.0, + "color": 4278238420, + "line_width": 2.0 + } + }, + { + "title": "Launcher P", + "x": 0.0, + "y": 608.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestP", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Launcher I", + "x": 128.0, + "y": 608.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestI", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Launcher D", + "x": 256.0, + "y": 608.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestD", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "MMAccel", + "x": 0.0, + "y": 704.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestMMAccel", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "MMJerk", + "x": 128.0, + "y": 704.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Launcher TestMMJerk", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Pneumatic Subsystem", + "x": 640.0, + "y": 576.0, + "width": 256.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Pneumatic Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Compressor Running", + "x": 640.0, + "y": 480.0, + "width": 128.0, + "height": 96.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Compressor Running", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Pressure", + "x": 768.0, + "y": 480.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pressure", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Set Speed", + "x": 1088.0, + "y": 96.0, + "width": 128.0, + "height": 96.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Intake TestSpeed", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Lift Run", + "x": 256.0, + "y": 0.0, + "width": 128.0, + "height": 96.0, + "type": "Toggle Switch", + "properties": { + "topic": "/SmartDashboard/Lift TestLiftRun", + "period": 0.06, + "data_type": "boolean" + } + }, + { + "title": "Subsystem", + "x": 256.0, + "y": 352.0, + "width": 128.0, + "height": 64.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Lift Subsystem", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsA.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsA.auto new file mode 100644 index 0000000..72bafba --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsA.auto @@ -0,0 +1,67 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "named", + "data": { + "name": "CrossBumpFwd" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_PickupFwd" + } + }, + { + "type": "named", + "data": { + "name": "RotateOtherSide" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_PickupBwd" + } + }, + { + "type": "named", + "data": { + "name": "CrossBumpBwd" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_ParkAtWall" + } + }, + { + "type": "named", + "data": { + "name": "Shoot" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_A_Bump" + } + }, + { + "type": "named", + "data": { + "name": "CrossBumpFwd" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsB.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsB.auto new file mode 100644 index 0000000..d26d255 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DepoSideAutoPathsB.auto @@ -0,0 +1,67 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "named", + "data": { + "name": "CrossBumpFwd" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_PickupFwd" + } + }, + { + "type": "named", + "data": { + "name": "RotateOtherSide" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_PickupBwd" + } + }, + { + "type": "named", + "data": { + "name": "CrossBumpBwd" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_ParkAtWall" + } + }, + { + "type": "named", + "data": { + "name": "Shoot" + } + }, + { + "type": "path", + "data": { + "pathName": "DSA_B_PickupDepo" + } + }, + { + "type": "named", + "data": { + "name": "Shoot" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/DriveForward.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DriveForward.auto new file mode 100644 index 0000000..983e8ed --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/DriveForward.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "DriveForward" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndReverse.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndReverse.auto new file mode 100644 index 0000000..e41fa5b --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndReverse.auto @@ -0,0 +1,31 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "GatherCorral1" + } + }, + { + "type": "path", + "data": { + "pathName": "GatherCorral2" + } + }, + { + "type": "named", + "data": { + "name": "AimToHub" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndShoot.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndShoot.auto new file mode 100644 index 0000000..07f8c4b --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/GatherCorralAndShoot.auto @@ -0,0 +1,25 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "GatherCorralShoot" + } + }, + { + "type": "named", + "data": { + "name": "AimToHub" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto new file mode 100644 index 0000000..62253ac --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto @@ -0,0 +1,25 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "ReverseIntoCorner" + } + }, + { + "type": "named", + "data": { + "name": "AimToHub" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorizontal.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorizontal.auto new file mode 100644 index 0000000..39521a1 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorizontal.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "Example_Path" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto new file mode 100644 index 0000000..a2e63bd --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "ExamplePathBackwards" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/autos/VEL_GatherCorralAndShoot.auto b/2026-Rebuilt/src/main/deploy/pathplanner/autos/VEL_GatherCorralAndShoot.auto new file mode 100644 index 0000000..9ac4b21 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/autos/VEL_GatherCorralAndShoot.auto @@ -0,0 +1,31 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "GatherCorralShootVel" + } + }, + { + "type": "path", + "data": { + "pathName": "GatherCorralVelTest" + } + }, + { + "type": "path", + "data": { + "pathName": "GatherCorralVelTest2" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/navgrid.json b/2026-Rebuilt/src/main/deploy/pathplanner/navgrid.json new file mode 100644 index 0000000..bb9b582 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/navgrid.json @@ -0,0 +1 @@ +{"field_size":{"x":16.54,"y":8.07},"nodeSizeMeters":0.3,"grid":[[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]]} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/2_Meter_New.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/2_Meter_New.path new file mode 100644 index 0000000..a1988a4 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/2_Meter_New.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 0.9999999999999996, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 8.0 + }, + "prevControl": { + "x": 1.0, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/ContinuousJ.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ContinuousJ.path new file mode 100644 index 0000000..0c2ae1f --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ContinuousJ.path @@ -0,0 +1,86 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 6.304833333333335, + "y": 5.430384615384615 + }, + "prevControl": null, + "nextControl": { + "x": 6.554833333333335, + "y": 5.430384615384615 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.654, + "y": 4.755948717948717 + }, + "prevControl": { + "x": 7.654, + "y": 5.477740925098693 + }, + "nextControl": { + "x": 7.654, + "y": 4.0836931462226715 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.654, + "y": 3.418705128205129 + }, + "prevControl": { + "x": 7.654, + "y": 3.943430491892358 + }, + "nextControl": { + "x": 7.654, + "y": 2.8939797645179 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.735051282051284, + "y": 2.8838076923076925 + }, + "prevControl": { + "x": 5.985051282051284, + "y": 2.8838076923076925 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_A_Bump.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_A_Bump.path new file mode 100644 index 0000000..0079ae4 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_A_Bump.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.63, + "y": 7.465 + }, + "prevControl": null, + "nextControl": { + "x": 0.7904815091648099, + "y": 7.273308359034138 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.5256923076923092, + "y": 5.930397435897436 + }, + "prevControl": { + "x": 2.2220361885581448, + "y": 5.930397435897436 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 1.0, + "rotation": 0.0 + }, + "reversed": false, + "folder": "DepotSideAuto", + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_B_PickupDepo.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_B_PickupDepo.path new file mode 100644 index 0000000..4eac969 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_B_PickupDepo.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.63, + "y": 7.465 + }, + "prevControl": null, + "nextControl": { + "x": 0.6177871629962063, + "y": 7.215298484961908 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.7581794871794887, + "y": 4.942 + }, + "prevControl": { + "x": 0.28142307692307866, + "y": 5.337358974358976 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": "DepotSideAuto", + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_ParkAtWall.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_ParkAtWall.path new file mode 100644 index 0000000..b088437 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_ParkAtWall.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.3047307692307704, + "y": 6.046679487179488 + }, + "prevControl": null, + "nextControl": { + "x": 1.3860289708154356, + "y": 6.046679487179488 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.6302692307692321, + "y": 7.4653205128205125 + }, + "prevControl": { + "x": 0.7359905971020129, + "y": 7.238774727821696 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.2, + "rotation": 0.0 + }, + "reversed": true, + "folder": "DepotSideAuto", + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupBwd.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupBwd.path new file mode 100644 index 0000000..9a7b16b --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupBwd.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 7.596, + "y": 5.151 + }, + "prevControl": null, + "nextControl": { + "x": 7.346, + "y": 5.151 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.688538461538462, + "y": 5.151 + }, + "prevControl": { + "x": 5.938538461538462, + "y": 5.151 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 1.0, + "rotation": 0.0 + }, + "reversed": true, + "folder": "DepotSideAuto", + "idealStartingState": { + "velocity": 1.0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupFwd.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupFwd.path new file mode 100644 index 0000000..5d3e540 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DSA_PickupFwd.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 6.095525641025643, + "y": 6.116448717948718 + }, + "prevControl": null, + "nextControl": { + "x": 7.363, + "y": 6.3606410256410255 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 7.595564102564104, + "y": 5.151307692307693 + }, + "prevControl": { + "x": 7.595564102564104, + "y": 5.401307692307693 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": "DepotSideAuto", + "idealStartingState": { + "velocity": 1.0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path new file mode 100644 index 0000000..ee56cf3 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.5, + "y": 6.3 + }, + "prevControl": null, + "nextControl": { + "x": 2.749847706754774, + "y": 6.291275125824375 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.5, + "y": 6.3 + }, + "prevControl": { + "x": 0.2501522932452258, + "y": 6.308724874175625 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForward.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForward.path new file mode 100644 index 0000000..c8313ec --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForward.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.293102564102565, + "y": 5.896 + }, + "prevControl": null, + "nextControl": { + "x": 3.293102564102565, + "y": 5.896 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.6070897435897447, + "y": 5.896 + }, + "prevControl": { + "x": 2.6070897435897447, + "y": 5.896 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path new file mode 100644 index 0000000..b806894 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.0, + "y": 6.28 + }, + "prevControl": null, + "nextControl": { + "x": 1.2499999048070625, + "y": 6.2797818338711915 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.252435897435897, + "y": 6.28 + }, + "prevControl": { + "x": 2.0028673041421476, + "y": 6.265319494512134 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveToCorner.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveToCorner.path new file mode 100644 index 0000000..07e6691 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/DriveToCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.2582179487179497, + "y": 5.535038461538461 + }, + "prevControl": null, + "nextControl": { + "x": 2.2931025641025653, + "y": 6.058307692307692 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.7, + "y": 6.5 + }, + "prevControl": { + "x": 2.2465897435897455, + "y": 6.139705128205128 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path new file mode 100644 index 0000000..928549c --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 4.587 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 3.814064102564102 + }, + "prevControl": { + "x": 2.0, + "y": 4.064064102564102 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/Example_Path.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Example_Path.path new file mode 100644 index 0000000..64d5745 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Example_Path.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 4.587 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 3.814064102564102 + }, + "prevControl": { + "x": 2.0, + "y": 4.064064102564102 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/FromDepoTowerLineup.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/FromDepoTowerLineup.path new file mode 100644 index 0000000..02a2950 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/FromDepoTowerLineup.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.84, + "y": 7.128 + }, + "prevControl": null, + "nextControl": { + "x": 1.148715910576948, + "y": 6.868956593296325 + }, + "isLocked": true, + "linkedName": null + }, + { + "anchor": { + "x": 1.432615384615386, + "y": 3.744294871794872 + }, + "prevControl": { + "x": 4.000377113133939, + "y": 3.7578283485045514 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 6.271077449501154 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral1.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral1.path new file mode 100644 index 0000000..73db8e3 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral1.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.7233205128205147, + "y": 4.942 + }, + "prevControl": null, + "nextControl": { + "x": 1.258192307692309, + "y": 4.96525641025641 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.5488717948717963, + "y": 5.883884615384615 + }, + "prevControl": { + "x": 0.4929110576923091, + "y": 5.378784455128205 + }, + "nextControl": { + "x": 0.6048325320512834, + "y": 6.388984775641026 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.479, + "y": 6.849 + }, + "prevControl": { + "x": 0.9442307692307704, + "y": 7.162987179487179 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral2.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral2.path new file mode 100644 index 0000000..a116043 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorral2.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.4791282051312882, + "y": 6.849025641019939 + }, + "prevControl": null, + "nextControl": { + "x": 1.2706819835318741, + "y": 6.987048004066396 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.6884102564102579, + "y": 7.349038461538462 + }, + "prevControl": { + "x": 1.0488846153846165, + "y": 7.011820512820513 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShoot.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShoot.path new file mode 100644 index 0000000..b090433 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShoot.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.7233205128205147, + "y": 4.942 + }, + "prevControl": null, + "nextControl": { + "x": 1.258192307692309, + "y": 4.96525641025641 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.5488717948717963, + "y": 5.883884615384615 + }, + "prevControl": { + "x": 0.4929118910589782, + "y": 5.378784362799069 + }, + "nextControl": { + "x": 0.6048316986846143, + "y": 6.388984867970162 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.6884102564102579, + "y": 6.651346153846154 + }, + "prevControl": { + "x": 0.6608812672599742, + "y": 6.402866467037828 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShootVel.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShootVel.path new file mode 100644 index 0000000..e1b3068 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralShootVel.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.7233205128205147, + "y": 4.942 + }, + "prevControl": null, + "nextControl": { + "x": 1.258192307692309, + "y": 4.96525641025641 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.5488717948717963, + "y": 5.883884615384615 + }, + "prevControl": { + "x": 0.4929118910589782, + "y": 5.378784362799069 + }, + "nextControl": { + "x": 0.6048316986846143, + "y": 6.388984867970162 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.6884102564102579, + "y": 6.651346153846154 + }, + "prevControl": { + "x": 0.6608812672599742, + "y": 6.402866467037828 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.5, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 1.0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest.path new file mode 100644 index 0000000..939b881 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.688, + "y": 6.651 + }, + "prevControl": null, + "nextControl": { + "x": 0.7155289891502836, + "y": 6.899479686808326 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.5954102564102572, + "y": 7.453692307692307 + }, + "prevControl": { + "x": 0.7368002260611529, + "y": 7.5411433293019385 + }, + "nextControl": { + "x": 4.107102564102565, + "y": 7.197871794871795 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.4791538461538476, + "y": 5.186192307692308 + }, + "prevControl": { + "x": 3.8163974358974375, + "y": 5.174564102564102 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0.5, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest2.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest2.path new file mode 100644 index 0000000..75c80e2 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherCorralVelTest2.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.479, + "y": 5.186 + }, + "prevControl": null, + "nextControl": { + "x": 3.479, + "y": 5.186 + }, + "isLocked": true, + "linkedName": null + }, + { + "anchor": { + "x": 3.5954615384615396, + "y": 5.186 + }, + "prevControl": { + "x": 3.0938000773498056, + "y": 5.186 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherDepot.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherDepot.path new file mode 100644 index 0000000..e7aeec2 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherDepot.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.1070512820512834, + "y": 4.942 + }, + "prevControl": null, + "nextControl": { + "x": 1.5023846153846168, + "y": 4.965256410256412 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.0140000000000011, + "y": 5.093166666666667 + }, + "prevControl": { + "x": 1.4363182059175275, + "y": 4.95259912183499 + }, + "nextControl": { + "x": -0.03267948717948488, + "y": 5.441551282051282 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.0140000000000011, + "y": 7.128102564102565 + }, + "prevControl": { + "x": 0.7349230769230779, + "y": 6.837397435897437 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS2.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS2.path new file mode 100644 index 0000000..86b39f4 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS2.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 6.607166666666667, + "y": 5.37224358974359 + }, + "prevControl": null, + "nextControl": { + "x": 8.025807692307692, + "y": 5.37224358974359 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.049, + "y": 4.093141025641025 + }, + "prevControl": { + "x": 8.025743589743588, + "y": 5.37224358974359 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": "Gather", + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS4.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS4.path new file mode 100644 index 0000000..2781161 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/GatherS4.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 8.049, + "y": 4.093 + }, + "prevControl": null, + "nextControl": { + "x": 7.735102564102567, + "y": 4.3489615384615385 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.455974358974361, + "y": 5.395499999999999 + }, + "prevControl": { + "x": 5.979243589743592, + "y": 5.37224358974359 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": "Gather", + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/LineupToTower.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/LineupToTower.path new file mode 100644 index 0000000..0a8712c --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/LineupToTower.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.6321826809015425, + "y": 4.895487179487179 + }, + "prevControl": null, + "nextControl": { + "x": 2.6321826809015425, + "y": 4.3574391836798885 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.2000769230769244, + "y": 3.8954615384615385 + }, + "prevControl": { + "x": 2.618692307692309, + "y": 4.104769230769231 + }, + "nextControl": { + "x": 1.907009241234153, + "y": 3.7489276975401524 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.5721538461538476, + "y": 3.755923076923077 + }, + "prevControl": { + "x": 1.8221538461538476, + "y": 3.755923076923077 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 131.18592516570953 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0.0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/ParkAtWall.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ParkAtWall.path new file mode 100644 index 0000000..b433301 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ParkAtWall.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.6070384615384627, + "y": 6.558320512820512 + }, + "prevControl": null, + "nextControl": { + "x": 1.4469923616223426, + "y": 6.750375832719857 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.6302692307692321, + "y": 7.4653205128205125 + }, + "prevControl": { + "x": 0.7359905971020129, + "y": 7.238774727821696 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 2.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0.2, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0.0, + "rotation": 0.0 + }, + "useDefaultConstraints": false +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path new file mode 100644 index 0000000..f5a63f4 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.7349487179487189, + "y": 6.104820512820513 + }, + "prevControl": null, + "nextControl": { + "x": 1.5521849008880018, + "y": 6.275400075410516 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.7930641025641041, + "y": 7.162987179487179 + }, + "prevControl": { + "x": 0.9791153846153859, + "y": 6.942051282051282 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": -62.96913974015706 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path new file mode 100644 index 0000000..b7dff0e --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": null, + "nextControl": { + "x": 0.43680380386361306, + "y": 6.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": { + "x": 2.0, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/S_Turn.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/S_Turn.path new file mode 100644 index 0000000..a8a0ad7 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/S_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": { + "x": 0.0, + "y": 6.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/Smooth_Turn.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Smooth_Turn.path new file mode 100644 index 0000000..639102e --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/Smooth_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": { + "x": 1.9999999999999998, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/TestPointsGet.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/TestPointsGet.path new file mode 100644 index 0000000..4e93b16 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/TestPointsGet.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 12.945, + "y": 4.0 + }, + "prevControl": null, + "nextControl": { + "x": -87.0550000000002, + "y": 4.000000000000012 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": -0.044, + "y": 0.0 + }, + "prevControl": { + "x": -1.044, + "y": 1.2246467991473532e-16 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/wawd.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/wawd.path new file mode 100644 index 0000000..8cf8bff --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/wawd.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 4.607, + "y": 4.035 + }, + "prevControl": null, + "nextControl": { + "x": 5.606999999999999, + "y": 4.035 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.304884615384616, + "y": 4.035 + }, + "prevControl": { + "x": 7.304884615384616, + "y": 4.035 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/paths/wdawda.path b/2026-Rebuilt/src/main/deploy/pathplanner/paths/wdawda.path new file mode 100644 index 0000000..74c7aba --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/paths/wdawda.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.5953846153846167, + "y": 6.267615384615385 + }, + "prevControl": null, + "nextControl": { + "x": 1.0256282051282062, + "y": 6.27924358974359 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": { + "x": 2.0, + "y": 5.355075362944116 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/deploy/pathplanner/settings.json b/2026-Rebuilt/src/main/deploy/pathplanner/settings.json new file mode 100644 index 0000000..8fa1e36 --- /dev/null +++ b/2026-Rebuilt/src/main/deploy/pathplanner/settings.json @@ -0,0 +1,35 @@ +{ + "robotWidth": 0.9, + "robotLength": 0.9, + "holonomicMode": false, + "pathFolders": [ + "Gather", + "DepotSideAuto" + ], + "autoFolders": [], + "defaultMaxVel": 1.0, + "defaultMaxAccel": 1.0, + "defaultMaxAngVel": 540.0, + "defaultMaxAngAccel": 720.0, + "defaultNominalVoltage": 12.0, + "robotMass": 74.088, + "robotMOI": 6.883, + "robotTrackwidth": 0.787, + "driveWheelRadius": 0.0508, + "driveGearing": 5.143, + "maxDriveSpeed": 5.45, + "driveMotorType": "krakenX60", + "driveCurrentLimit": 60.0, + "wheelCOF": 1.0, + "flModuleX": 0.273, + "flModuleY": 0.273, + "frModuleX": 0.273, + "frModuleY": -0.273, + "blModuleX": -0.273, + "blModuleY": 0.273, + "brModuleX": -0.273, + "brModuleY": -0.273, + "bumperOffsetX": 0.0, + "bumperOffsetY": 0.0, + "robotFeatures": [] +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/java/frc/robot/Constants.java b/2026-Rebuilt/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..82ca6c0 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/Constants.java @@ -0,0 +1,259 @@ +// 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. + +// +// CANIDs and other hardware resource IDs and channels are defined in +// the robot specification document at https://docs.google.com/document/d/1A_Mh49vCdzeZFZrrAAlE3kgAf9tdAOItZ7-rJoq3Ufs/edit?usp=sharing +// +// Current CAN IDs: +// +// 01 - Pneumatics hub +// 02 - Pigeon 2 +// 10 - Drive left primary +// 11 - Drive left follower +// 20 - Drive right primary +// 21 - Drive right follower +// 30 - Intake roller +// 40 - Launcher flywheel primary motor +// 41 - Launcher flywheel secondary motor +// 42 - Launcher lift belt motor +// 50 - Indexer motor +// 51 - Bulk move belt motor +// +// Current DIO Channels: +// +// 0 - Upper fuel sensor (launcher subsystem) +// 1 - +// +// Current Solenoid Channels: +// +// 0 - Intake double channel 1 +// 1 - Intake double channel 2 +// 2 - Climb L1 Left +// 3 - Climb L1 Right + +package frc.robot; + +import java.util.Map; + +import com.pathplanner.lib.config.ModuleConfig; +import com.pathplanner.lib.config.RobotConfig; + +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.system.plant.DCMotor; +import edu.wpi.first.wpilibj.PneumaticsModuleType; +import edu.wpi.first.wpilibj.DoubleSolenoid; + +/** + * 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 class FieldConstants { + public static final Pose2d kBlueHubCenter = new Pose2d(4.607, 4.035, new Rotation2d()); + public static final Pose2d kRedHubCenter = new Pose2d(11.9284, 4.035, new Rotation2d()); + + public static final double kFieldWidth = 16.540988; // meters, x axis. + public static final double kFieldLength = 8.069326; // meters, y axis. + } + + public static class RobotConstants { + public static final int kPigeon2CANID = 2; + public static final boolean kDoPigeonWarn = true; + public static final double kPigeonYawOffset = -90.00460052490234; // degrees (-360, 360) + public static final double kPigeonPitchOffset = 1.5642017126083374; // degrees (-360, 360) + public static final double kPigeonRollOffset = 89.49134826660156; // degrees (-360, 360) + + public static enum kStartingNames { + DEPOT_SIDE, + HUB, + OUTPOST_SIDE + } + + public static final Map> kStartingPositions = Map.ofEntries( + Map.entry(kStartingNames.DEPOT_SIDE, Map.ofEntries( + Map.entry(true, new Pose2d(new Translation2d(12.956, 2.884), Rotation2d.k180deg)), // red + Map.entry(false, new Pose2d(new Translation2d(3.607, 5.186), Rotation2d.kZero)) // blue + )), + Map.entry(kStartingNames.HUB, Map.ofEntries( + Map.entry(true, new Pose2d(new Translation2d(12.945, 4.0), Rotation2d.k180deg)), // red + Map.entry(false, new Pose2d(new Translation2d(3.561, 4.0), Rotation2d.kZero)) // blue + )), + Map.entry(kStartingNames.OUTPOST_SIDE, Map.ofEntries( + Map.entry(true, new Pose2d(new Translation2d(12.979, 5.163), Rotation2d.k180deg)), // red + Map.entry(false, new Pose2d(new Translation2d(3.6, 2.9), Rotation2d.kZero)) // blue + )) + ); + + public static final double kRotateToHubSpeed = 3.0; + } + + public static class AutoConstants { + public static final double kMaxDriveVelocityMPS = 40.0; // true max speed of the robot mps + public static final double kWheelCOF = 1.0; // no data for this, bsed it + + public static final double kMassKG = 15.0; + + // Uncommenting this causes the weird x y rotion error. + //public static final double kMOI = (1/12) * kMassKG * ((DrivetrainConstants.kLengthMeters*DrivetrainConstants.kLengthMeters) + (DrivetrainConstants.kWidthMeters*DrivetrainConstants.kWidthMeters)); // Moment of Intertia + public static final double kMOI = kMassKG * (DrivetrainConstants.kTrackWidthMeters/2) * (DrivetrainConstants.kA_angular / DrivetrainConstants.kA_linear); // Moment of Intertia + + public static final double kDriveCurrentLimit = 10.0; // amps + + // These values may change per bot, we might want to populate these on init w/ accurate data + public static final int kNumMotors = 4; // # of motors in a gearbox + + public static final DCMotor kDriveMotor = DCMotor.getKrakenX60(kNumMotors); + public static final ModuleConfig kModuleConfig = new ModuleConfig( + DrivetrainConstants.kWheelRadiusMeters, + kMaxDriveVelocityMPS, + kWheelCOF, + kDriveMotor, + kDriveCurrentLimit, + kNumMotors + ); + + public static final RobotConfig kRobotConfig = new RobotConfig(kMassKG, kMOI, kModuleConfig, DrivetrainConstants.kTrackWidthMeters); + // populate on init possibility end here + } + + public static class PneumaticsConstants { + public static final PneumaticsModuleType kHubType = PneumaticsModuleType.REVPH; + public static final int kPneumaticsHubCANID = 1; + public static final double kMinPneumaticsPressure = 80.0; + public static final double kMaxPneumaticsPressure = 115.0; + + public static final int kTicksPerUpdate = 5; + } + + public static class OperatorConstants { + public static final int kDriverControllerPort = 0; + public static final double kDriverControllerDeadband = 0.02; // Exclusive + + public static final int kOperatorControllerPort = 1; + } + + public static class DrivetrainConstants { + public static final int kLeftMotorCANID = 10; + public static final int kOptionalLeftMotorCANID = 11; + + public static final int kRightMotorCANID = 20; + public static final int kOptionalRightMotorCANID = 21; + + public static final double kTurnDivider = 2.5; + public static final double kSpeedDivider = 2.5; + + // These numbers came from the ctre example then tweaked + public static final double kS = 0.1; // A velocity target of 1 rps results in xV output + public static final double kV = 0.12; // Add x V output to overcome static friction + public static final double kP = 0.11; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kA_linear = 0.01; // Voltage needed to induce a given accel. in the motor shaft + public static final double kA_angular = 0.01; + public static final double kPeakVoltage = 8.0; + + public static final double kMaxVelocityMPS = 3.0; // 6 mps is the max of the motors during zero load. + public static final double kMaxAccelerationMPS2 = 5.0; // M/S^2 + + public static final double kMotionMagicAcceleration = 100.0; // Higher number --> Faster (50.0 = ~1s to max) + public static final double kMotionMagicJerk = 4000.0; + + // For Auto Potentially + public static boolean kLeftPositiveMovesForward = true; + public static boolean kRightPositiveMovesForward = true; + + // Physical measurements related to the drivetrain. + public static final double kDrivetrainGearRatio = 0.2; + public static final double kWheelRadiusMeters = (4.0 / 2.0) * 0.0254; // Four Inch Wheels + public static final double kWheelCircumference = 2 * Math.PI * DrivetrainConstants.kWheelRadiusMeters; + public static final double kBumpersMeters = 0.0889; + public static final double kWidthMeters = 0.8636-kBumpersMeters; // width w/ bumpers - bumpers + public static final double kLengthMeters = 0.8636-kBumpersMeters; + public static final double kTrackWidthMeters = kLengthMeters;//29.0 * 0.0254;//0.74; + + // SmartDashboard update frequency for drive subsystem state in 20ms counts. + public static final int kTicksPerUpdate = 5; + } + + public static class IntakeConstants { + public static int kIntakeMotorCANID = 30; + + // Raw intake motor speed in range [-1.0,1.0] + public static double kIntakeMotorSpeed = 0.6; + + // Solenoid states required to extend and retract the intake mechanism. + public static int kIntakeSolenoidForward = 15; + public static int kIntakeSolenoidReverse = 13; + public static DoubleSolenoid.Value kIntakeExtend = DoubleSolenoid.Value.kForward; + public static DoubleSolenoid.Value kIntakeRetract = DoubleSolenoid.Value.kReverse; + } + + public static class LauncherConstants { + public static final int kLauncherFlywheelMotor1CANID = 40; + public static final int kLauncherFlywheelMotor2CANID = 41; + public static final int kLauncherLiftMotorCANID = 42; + + // The speed, in range [-1.0, 1.0], to run the lift motor when started. + public static final double kLiftMotorSpeed = 1.0; + + // Beam break sensor to detect fuel at the top of the lift. + public static final int kUpperFuelSensorChannel = 0; + public static final boolean kUpperFuelSensorIsEmpty = true; + + // These numbers came from the ctre example then tweaked + public static final double kS = 0.1; // A velocity target of 1 rps results in xV output + public static final double kV = 0.12; // Add x V output to overcome static friction + public static final double kP = 0.11; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kA_linear = 0.01; // Voltage needed to induce a given accel. in the motor shaft + public static final double kA_angular = 0.01; + public static final double kPeakVoltage = 8.0; + + public static final double kMaxVelocityRPS = 6000.0/60.0; + public static final double kMaxAccelerationRPS2 = 50.0; + + public static final double kMotionMagicAcceleration = 50.0; // Higher number --> Faster (50.0 = ~1s to max) + public static final double kMotionMagicJerk = 4000.0; + + // Set to false if both flywheel motors drive in the same direction, false if they + // run in opposite directions. + public static final boolean kMotorsDriveInOppositeDirections = true; + + // Set to false if we drive the lead launcher flywheel motor clockwise to operate + // correctly, or true to drive it counterclockwise. + public static final boolean kLauncherMotorForwardIsCCW = false; + + // Frequency at which we send current launcher speed back to the driver station. + public static final int kTicksPerUpdate = 10; + public static final int kTicksPerDistanceUpdate = 12; + + public static final double kFixedTestSpeed = 3000.0; + public static final double kFlywheelToleranceRPM = 100.0; + } + + public static class IndexerBulkConstants { + public static int kIndexerMotorCANID = 50; + public static int kBulkMoveMotorCANID = 51; + + // Raw motor speeds in range [-1.0,1.0] + public static final double kBulkMoveMotorSpeed = 0.5; + public static final double kIndexerMotorSpeed = 0.5; + } + + public static class ClimbL1Constants { + public static int kLeftSolenoidChannel = 1; + public static int kRightSolenoidChannel = 2; + + public static boolean kClimbL1Raise = true; + public static boolean kClimbL1Lower = !kClimbL1Raise; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/LimelightHelpers.java b/2026-Rebuilt/src/main/java/frc/robot/LimelightHelpers.java new file mode 100644 index 0000000..1dbf387 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/LimelightHelpers.java @@ -0,0 +1,1645 @@ +//LimelightHelpers v1.11 (REQUIRES LLOS 2025.0 OR LATER) + +package frc.robot; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.DoubleArrayEntry; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.TimestampedDoubleArray; + +/** + * LimelightHelpers provides static methods and classes for interfacing with Limelight vision cameras in FRC. + * This library supports all Limelight features including AprilTag tracking, Neural Networks, and standard color/retroreflective tracking. + */ +public class LimelightHelpers { + + private static final Map doubleArrayEntries = new ConcurrentHashMap<>(); + + /** + * Represents a Color/Retroreflective Target Result extracted from JSON Output + */ + public static class LimelightTarget_Retro { + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Retro() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + + } + + /** + * Represents an AprilTag/Fiducial Target Result extracted from JSON Output + */ + public static class LimelightTarget_Fiducial { + + @JsonProperty("fID") + public double fiducialID; + + @JsonProperty("fam") + public String fiducialFamily; + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Fiducial() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + } + + /** + * Represents a Barcode Target Result extracted from JSON Output + */ + public static class LimelightTarget_Barcode { + + /** + * Barcode family type (e.g. "QR", "DataMatrix", etc.) + */ + @JsonProperty("fam") + public String family; + + /** + * Gets the decoded data content of the barcode + */ + @JsonProperty("data") + public String data; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("pts") + public double[][] corners; + + public LimelightTarget_Barcode() { + } + + public String getFamily() { + return family; + } + } + + /** + * Represents a Neural Classifier Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Classifier { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("zone") + public double zone; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("typ") + public double ty_pixels; + + public LimelightTarget_Classifier() { + } + } + + /** + * Represents a Neural Detector Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Detector { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + public LimelightTarget_Detector() { + } + } + + /** + * Limelight Results object, parsed from a Limelight's JSON results output. + */ + public static class LimelightResults { + + public String error; + + @JsonProperty("pID") + public double pipelineID; + + @JsonProperty("tl") + public double latency_pipeline; + + @JsonProperty("cl") + public double latency_capture; + + public double latency_jsonParse; + + @JsonProperty("ts") + public double timestamp_LIMELIGHT_publish; + + @JsonProperty("ts_rio") + public double timestamp_RIOFPGA_capture; + + @JsonProperty("v") + @JsonFormat(shape = Shape.NUMBER) + public boolean valid; + + @JsonProperty("botpose") + public double[] botpose; + + @JsonProperty("botpose_wpired") + public double[] botpose_wpired; + + @JsonProperty("botpose_wpiblue") + public double[] botpose_wpiblue; + + @JsonProperty("botpose_tagcount") + public double botpose_tagcount; + + @JsonProperty("botpose_span") + public double botpose_span; + + @JsonProperty("botpose_avgdist") + public double botpose_avgdist; + + @JsonProperty("botpose_avgarea") + public double botpose_avgarea; + + @JsonProperty("t6c_rs") + public double[] camerapose_robotspace; + + public Pose3d getBotPose3d() { + return toPose3D(botpose); + } + + public Pose3d getBotPose3d_wpiRed() { + return toPose3D(botpose_wpired); + } + + public Pose3d getBotPose3d_wpiBlue() { + return toPose3D(botpose_wpiblue); + } + + public Pose2d getBotPose2d() { + return toPose2D(botpose); + } + + public Pose2d getBotPose2d_wpiRed() { + return toPose2D(botpose_wpired); + } + + public Pose2d getBotPose2d_wpiBlue() { + return toPose2D(botpose_wpiblue); + } + + @JsonProperty("Retro") + public LimelightTarget_Retro[] targets_Retro; + + @JsonProperty("Fiducial") + public LimelightTarget_Fiducial[] targets_Fiducials; + + @JsonProperty("Classifier") + public LimelightTarget_Classifier[] targets_Classifier; + + @JsonProperty("Detector") + public LimelightTarget_Detector[] targets_Detector; + + @JsonProperty("Barcode") + public LimelightTarget_Barcode[] targets_Barcode; + + public LimelightResults() { + botpose = new double[6]; + botpose_wpired = new double[6]; + botpose_wpiblue = new double[6]; + camerapose_robotspace = new double[6]; + targets_Retro = new LimelightTarget_Retro[0]; + targets_Fiducials = new LimelightTarget_Fiducial[0]; + targets_Classifier = new LimelightTarget_Classifier[0]; + targets_Detector = new LimelightTarget_Detector[0]; + targets_Barcode = new LimelightTarget_Barcode[0]; + + } + + + } + + /** + * Represents a Limelight Raw Fiducial result from Limelight's NetworkTables output. + */ + public static class RawFiducial { + public int id = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double distToCamera = 0; + public double distToRobot = 0; + public double ambiguity = 0; + + + public RawFiducial(int id, double txnc, double tync, double ta, double distToCamera, double distToRobot, double ambiguity) { + this.id = id; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.distToCamera = distToCamera; + this.distToRobot = distToRobot; + this.ambiguity = ambiguity; + } + } + + /** + * Represents a Limelight Raw Neural Detector result from Limelight's NetworkTables output. + */ + public static class RawDetection { + public int classId = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double corner0_X = 0; + public double corner0_Y = 0; + public double corner1_X = 0; + public double corner1_Y = 0; + public double corner2_X = 0; + public double corner2_Y = 0; + public double corner3_X = 0; + public double corner3_Y = 0; + + + public RawDetection(int classId, double txnc, double tync, double ta, + double corner0_X, double corner0_Y, + double corner1_X, double corner1_Y, + double corner2_X, double corner2_Y, + double corner3_X, double corner3_Y ) { + this.classId = classId; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.corner0_X = corner0_X; + this.corner0_Y = corner0_Y; + this.corner1_X = corner1_X; + this.corner1_Y = corner1_Y; + this.corner2_X = corner2_X; + this.corner2_Y = corner2_Y; + this.corner3_X = corner3_X; + this.corner3_Y = corner3_Y; + } + } + + /** + * Represents a 3D Pose Estimate. + */ + public static class PoseEstimate { + public Pose2d pose; + public double timestampSeconds; + public double latency; + public int tagCount; + public double tagSpan; + public double avgTagDist; + public double avgTagArea; + + public RawFiducial[] rawFiducials; + public boolean isMegaTag2; + + /** + * Instantiates a PoseEstimate object with default values + */ + public PoseEstimate() { + this.pose = new Pose2d(); + this.timestampSeconds = 0; + this.latency = 0; + this.tagCount = 0; + this.tagSpan = 0; + this.avgTagDist = 0; + this.avgTagArea = 0; + this.rawFiducials = new RawFiducial[]{}; + this.isMegaTag2 = false; + } + + public PoseEstimate(Pose2d pose, double timestampSeconds, double latency, + int tagCount, double tagSpan, double avgTagDist, + double avgTagArea, RawFiducial[] rawFiducials, boolean isMegaTag2) { + + this.pose = pose; + this.timestampSeconds = timestampSeconds; + this.latency = latency; + this.tagCount = tagCount; + this.tagSpan = tagSpan; + this.avgTagDist = avgTagDist; + this.avgTagArea = avgTagArea; + this.rawFiducials = rawFiducials; + this.isMegaTag2 = isMegaTag2; + } + + } + + /** + * Encapsulates the state of an internal Limelight IMU. + */ + public static class IMUData { + public double robotYaw = 0.0; + public double Roll = 0.0; + public double Pitch = 0.0; + public double Yaw = 0.0; + public double gyroX = 0.0; + public double gyroY = 0.0; + public double gyroZ = 0.0; + public double accelX = 0.0; + public double accelY = 0.0; + public double accelZ = 0.0; + + public IMUData() {} + + public IMUData(double[] imuData) { + if (imuData != null && imuData.length >= 10) { + this.robotYaw = imuData[0]; + this.Roll = imuData[1]; + this.Pitch = imuData[2]; + this.Yaw = imuData[3]; + this.gyroX = imuData[4]; + this.gyroY = imuData[5]; + this.gyroZ = imuData[6]; + this.accelX = imuData[7]; + this.accelY = imuData[8]; + this.accelZ = imuData[9]; + } + } + } + + + private static ObjectMapper mapper; + + /** + * Print JSON Parse time to the console in milliseconds + */ + static boolean profileJSON = false; + + static final String sanitizeName(String name) { + if (name == "" || name == null) { + return "limelight"; + } + return name; + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose3d object. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose3d object representing the pose, or empty Pose3d if invalid data + */ + public static Pose3d toPose3D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 3D Pose Data!"); + return new Pose3d(); + } + return new Pose3d( + new Translation3d(inData[0], inData[1], inData[2]), + new Rotation3d(Units.degreesToRadians(inData[3]), Units.degreesToRadians(inData[4]), + Units.degreesToRadians(inData[5]))); + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose2d object. + * Uses only x, y, and yaw components, ignoring z, roll, and pitch. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose2d object representing the pose, or empty Pose2d if invalid data + */ + public static Pose2d toPose2D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 2D Pose Data!"); + return new Pose2d(); + } + Translation2d tran2d = new Translation2d(inData[0], inData[1]); + Rotation2d r2d = new Rotation2d(Units.degreesToRadians(inData[5])); + return new Pose2d(tran2d, r2d); + } + + /** + * Converts a Pose3d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * + * @param pose The Pose3d object to convert + * @return A 6-element array containing [x, y, z, roll, pitch, yaw] + */ + public static double[] pose3dToArray(Pose3d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = pose.getTranslation().getZ(); + result[3] = Units.radiansToDegrees(pose.getRotation().getX()); + result[4] = Units.radiansToDegrees(pose.getRotation().getY()); + result[5] = Units.radiansToDegrees(pose.getRotation().getZ()); + return result; + } + + /** + * Converts a Pose2d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * Note: z, roll, and pitch will be 0 since Pose2d only contains x, y, and yaw. + * + * @param pose The Pose2d object to convert + * @return A 6-element array containing [x, y, 0, 0, 0, yaw] + */ + public static double[] pose2dToArray(Pose2d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = 0; + result[3] = Units.radiansToDegrees(0); + result[4] = Units.radiansToDegrees(0); + result[5] = Units.radiansToDegrees(pose.getRotation().getRadians()); + return result; + } + + private static double extractArrayEntry(double[] inData, int position){ + if(inData.length < position+1) + { + return 0; + } + return inData[position]; + } + + private static PoseEstimate getBotPoseEstimate(String limelightName, String entryName, boolean isMegaTag2) { + DoubleArrayEntry poseEntry = LimelightHelpers.getLimelightDoubleArrayEntry(limelightName, entryName); + + TimestampedDoubleArray tsValue = poseEntry.getAtomic(); + double[] poseArray = tsValue.value; + long timestamp = tsValue.timestamp; + + if (poseArray.length == 0) { + // Handle the case where no data is available + return null; // or some default PoseEstimate + } + + var pose = toPose2D(poseArray); + double latency = extractArrayEntry(poseArray, 6); + int tagCount = (int)extractArrayEntry(poseArray, 7); + double tagSpan = extractArrayEntry(poseArray, 8); + double tagDist = extractArrayEntry(poseArray, 9); + double tagArea = extractArrayEntry(poseArray, 10); + + // Convert server timestamp from microseconds to seconds and adjust for latency + double adjustedTimestamp = (timestamp / 1000000.0) - (latency / 1000.0); + + RawFiducial[] rawFiducials = new RawFiducial[tagCount]; + int valsPerFiducial = 7; + int expectedTotalVals = 11 + valsPerFiducial * tagCount; + + if (poseArray.length != expectedTotalVals) { + // Don't populate fiducials + } else { + for(int i = 0; i < tagCount; i++) { + int baseIndex = 11 + (i * valsPerFiducial); + int id = (int)poseArray[baseIndex]; + double txnc = poseArray[baseIndex + 1]; + double tync = poseArray[baseIndex + 2]; + double ta = poseArray[baseIndex + 3]; + double distToCamera = poseArray[baseIndex + 4]; + double distToRobot = poseArray[baseIndex + 5]; + double ambiguity = poseArray[baseIndex + 6]; + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + } + + return new PoseEstimate(pose, adjustedTimestamp, latency, tagCount, tagSpan, tagDist, tagArea, rawFiducials, isMegaTag2); + } + + /** + * Gets the latest raw fiducial/AprilTag detection results from NetworkTables. + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawFiducial objects containing detection details + */ + public static RawFiducial[] getRawFiducials(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawfiducials"); + var rawFiducialArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 7; + if (rawFiducialArray.length % valsPerEntry != 0) { + return new RawFiducial[0]; + } + + int numFiducials = rawFiducialArray.length / valsPerEntry; + RawFiducial[] rawFiducials = new RawFiducial[numFiducials]; + + for (int i = 0; i < numFiducials; i++) { + int baseIndex = i * valsPerEntry; + int id = (int) extractArrayEntry(rawFiducialArray, baseIndex); + double txnc = extractArrayEntry(rawFiducialArray, baseIndex + 1); + double tync = extractArrayEntry(rawFiducialArray, baseIndex + 2); + double ta = extractArrayEntry(rawFiducialArray, baseIndex + 3); + double distToCamera = extractArrayEntry(rawFiducialArray, baseIndex + 4); + double distToRobot = extractArrayEntry(rawFiducialArray, baseIndex + 5); + double ambiguity = extractArrayEntry(rawFiducialArray, baseIndex + 6); + + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + + return rawFiducials; + } + + /** + * Gets the latest raw neural detector results from NetworkTables + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawDetection objects containing detection details + */ + public static RawDetection[] getRawDetections(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawdetections"); + var rawDetectionArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 12; + if (rawDetectionArray.length % valsPerEntry != 0) { + return new RawDetection[0]; + } + + int numDetections = rawDetectionArray.length / valsPerEntry; + RawDetection[] rawDetections = new RawDetection[numDetections]; + + for (int i = 0; i < numDetections; i++) { + int baseIndex = i * valsPerEntry; // Starting index for this detection's data + int classId = (int) extractArrayEntry(rawDetectionArray, baseIndex); + double txnc = extractArrayEntry(rawDetectionArray, baseIndex + 1); + double tync = extractArrayEntry(rawDetectionArray, baseIndex + 2); + double ta = extractArrayEntry(rawDetectionArray, baseIndex + 3); + double corner0_X = extractArrayEntry(rawDetectionArray, baseIndex + 4); + double corner0_Y = extractArrayEntry(rawDetectionArray, baseIndex + 5); + double corner1_X = extractArrayEntry(rawDetectionArray, baseIndex + 6); + double corner1_Y = extractArrayEntry(rawDetectionArray, baseIndex + 7); + double corner2_X = extractArrayEntry(rawDetectionArray, baseIndex + 8); + double corner2_Y = extractArrayEntry(rawDetectionArray, baseIndex + 9); + double corner3_X = extractArrayEntry(rawDetectionArray, baseIndex + 10); + double corner3_Y = extractArrayEntry(rawDetectionArray, baseIndex + 11); + + rawDetections[i] = new RawDetection(classId, txnc, tync, ta, corner0_X, corner0_Y, corner1_X, corner1_Y, corner2_X, corner2_Y, corner3_X, corner3_Y); + } + + return rawDetections; + } + + /** + * Prints detailed information about a PoseEstimate to standard output. + * Includes timestamp, latency, tag count, tag span, average tag distance, + * average tag area, and detailed information about each detected fiducial. + * + * @param pose The PoseEstimate object to print. If null, prints "No PoseEstimate available." + */ + public static void printPoseEstimate(PoseEstimate pose) { + if (pose == null) { + System.out.println("No PoseEstimate available."); + return; + } + + System.out.printf("Pose Estimate Information:%n"); + System.out.printf("Timestamp (Seconds): %.3f%n", pose.timestampSeconds); + System.out.printf("Latency: %.3f ms%n", pose.latency); + System.out.printf("Tag Count: %d%n", pose.tagCount); + System.out.printf("Tag Span: %.2f meters%n", pose.tagSpan); + System.out.printf("Average Tag Distance: %.2f meters%n", pose.avgTagDist); + System.out.printf("Average Tag Area: %.2f%% of image%n", pose.avgTagArea); + System.out.printf("Is MegaTag2: %b%n", pose.isMegaTag2); + System.out.println(); + + if (pose.rawFiducials == null || pose.rawFiducials.length == 0) { + System.out.println("No RawFiducials data available."); + return; + } + + System.out.println("Raw Fiducials Details:"); + for (int i = 0; i < pose.rawFiducials.length; i++) { + RawFiducial fiducial = pose.rawFiducials[i]; + System.out.printf(" Fiducial #%d:%n", i + 1); + System.out.printf(" ID: %d%n", fiducial.id); + System.out.printf(" TXNC: %.2f%n", fiducial.txnc); + System.out.printf(" TYNC: %.2f%n", fiducial.tync); + System.out.printf(" TA: %.2f%n", fiducial.ta); + System.out.printf(" Distance to Camera: %.2f meters%n", fiducial.distToCamera); + System.out.printf(" Distance to Robot: %.2f meters%n", fiducial.distToRobot); + System.out.printf(" Ambiguity: %.2f%n", fiducial.ambiguity); + System.out.println(); + } + } + + public static Boolean validPoseEstimate(PoseEstimate pose) { + return pose != null && pose.rawFiducials != null && pose.rawFiducials.length != 0; + } + + public static NetworkTable getLimelightNTTable(String tableName) { + return NetworkTableInstance.getDefault().getTable(sanitizeName(tableName)); + } + + public static void Flush() { + NetworkTableInstance.getDefault().flush(); + } + + public static NetworkTableEntry getLimelightNTTableEntry(String tableName, String entryName) { + return getLimelightNTTable(tableName).getEntry(entryName); + } + + public static DoubleArrayEntry getLimelightDoubleArrayEntry(String tableName, String entryName) { + String key = tableName + "/" + entryName; + return doubleArrayEntries.computeIfAbsent(key, k -> { + NetworkTable table = getLimelightNTTable(tableName); + return table.getDoubleArrayTopic(entryName).getEntry(new double[0]); + }); + } + + public static double getLimelightNTDouble(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDouble(0.0); + } + + public static void setLimelightNTDouble(String tableName, String entryName, double val) { + getLimelightNTTableEntry(tableName, entryName).setDouble(val); + } + + public static void setLimelightNTDoubleArray(String tableName, String entryName, double[] val) { + getLimelightNTTableEntry(tableName, entryName).setDoubleArray(val); + } + + public static double[] getLimelightNTDoubleArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDoubleArray(new double[0]); + } + + + public static String getLimelightNTString(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getString(""); + } + + public static String[] getLimelightNTStringArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getStringArray(new String[0]); + } + + + public static URL getLimelightURLString(String tableName, String request) { + String urlString = "http://" + sanitizeName(tableName) + ".local:5807/" + request; + URL url; + try { + url = new URL(urlString); + return url; + } catch (MalformedURLException e) { + System.err.println("bad LL URL"); + } + return null; + } + ///// + ///// + + /** + * Does the Limelight have a valid target? + * @param limelightName Name of the Limelight camera ("" for default) + * @return True if a valid target is present, false otherwise + */ + public static boolean getTV(String limelightName) { + return 1.0 == getLimelightNTDouble(limelightName, "tv"); + } + + /** + * Gets the horizontal offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTX(String limelightName) { + return getLimelightNTDouble(limelightName, "tx"); + } + + /** + * Gets the vertical offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTY(String limelightName) { + return getLimelightNTDouble(limelightName, "ty"); + } + + /** + * Gets the horizontal offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTXNC(String limelightName) { + return getLimelightNTDouble(limelightName, "txnc"); + } + + /** + * Gets the vertical offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTYNC(String limelightName) { + return getLimelightNTDouble(limelightName, "tync"); + } + + /** + * Gets the target area as a percentage of the image (0-100%). + * @param limelightName Name of the Limelight camera ("" for default) + * @return Target area percentage (0-100) + */ + public static double getTA(String limelightName) { + return getLimelightNTDouble(limelightName, "ta"); + } + + /** + * T2D is an array that contains several targeting metrcis + * @param limelightName Name of the Limelight camera + * @return Array containing [targetValid, targetCount, targetLatency, captureLatency, tx, ty, txnc, tync, ta, tid, targetClassIndexDetector, + * targetClassIndexClassifier, targetLongSidePixels, targetShortSidePixels, targetHorizontalExtentPixels, targetVerticalExtentPixels, targetSkewDegrees] + */ + public static double[] getT2DArray(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "t2d"); + } + + /** + * Gets the number of targets currently detected. + * @param limelightName Name of the Limelight camera + * @return Number of detected targets + */ + public static int getTargetCount(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[1]; + } + return 0; + } + + /** + * Gets the classifier class index from the currently running neural classifier pipeline + * @param limelightName Name of the Limelight camera + * @return Class index from classifier pipeline + */ + public static int getClassifierClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[10]; + } + return 0; + } + + /** + * Gets the detector class index from the primary result of the currently running neural detector pipeline. + * @param limelightName Name of the Limelight camera + * @return Class index from detector pipeline + */ + public static int getDetectorClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[11]; + } + return 0; + } + + /** + * Gets the current neural classifier result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from classifier pipeline + */ + public static String getClassifierClass (String limelightName) { + return getLimelightNTString(limelightName, "tcclass"); + } + + /** + * Gets the primary neural detector result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from detector pipeline + */ + public static String getDetectorClass (String limelightName) { + return getLimelightNTString(limelightName, "tdclass"); + } + + /** + * Gets the pipeline's processing latency contribution. + * @param limelightName Name of the Limelight camera + * @return Pipeline latency in milliseconds + */ + public static double getLatency_Pipeline(String limelightName) { + return getLimelightNTDouble(limelightName, "tl"); + } + + /** + * Gets the capture latency. + * @param limelightName Name of the Limelight camera + * @return Capture latency in milliseconds + */ + public static double getLatency_Capture(String limelightName) { + return getLimelightNTDouble(limelightName, "cl"); + } + + /** + * Gets the active pipeline index. + * @param limelightName Name of the Limelight camera + * @return Current pipeline index (0-9) + */ + public static double getCurrentPipelineIndex(String limelightName) { + return getLimelightNTDouble(limelightName, "getpipe"); + } + + /** + * Gets the current pipeline type. + * @param limelightName Name of the Limelight camera + * @return Pipeline type string (e.g. "retro", "apriltag", etc) + */ + public static String getCurrentPipelineType(String limelightName) { + return getLimelightNTString(limelightName, "getpipetype"); + } + + /** + * Gets the full JSON results dump. + * @param limelightName Name of the Limelight camera + * @return JSON string containing all current results + */ + public static String getJSONDump(String limelightName) { + return getLimelightNTString(limelightName, "json"); + } + + /** + * Switch to getBotPose + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + /** + * Switch to getBotPose_wpiRed + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + /** + * Switch to getBotPose_wpiBlue + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + public static double[] getBotPose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + public static double[] getBotPose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + } + + public static double[] getCameraPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + } + + public static double[] getTargetPose_CameraSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + } + + public static double[] getTargetPose_RobotSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + } + + public static double[] getTargetColor(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "tc"); + } + + public static double getFiducialID(String limelightName) { + return getLimelightNTDouble(limelightName, "tid"); + } + + public static String getNeuralClassID(String limelightName) { + return getLimelightNTString(limelightName, "tclass"); + } + + public static String[] getRawBarcodeData(String limelightName) { + return getLimelightNTStringArray(limelightName, "rawbarcodes"); + } + + ///// + ///// + + public static Pose3d getBotPose3d(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose"); + return toPose3D(poseArray); + } + + /** + * (Not Recommended) Gets the robot's 3D pose in the WPILib Red Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Red Alliance field space + */ + public static Pose3d getBotPose3d_wpiRed(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + return toPose3D(poseArray); + } + + /** + * (Recommended) Gets the robot's 3D pose in the WPILib Blue Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Blue Alliance field space + */ + public static Pose3d getBotPose3d_wpiBlue(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + return toPose3D(poseArray); + } + + /** + * Gets the robot's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation relative to the target + */ + public static Pose3d getBotPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the target + */ + public static Pose3d getCameraPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the camera's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the camera + */ + public static Pose3d getTargetPose3d_CameraSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the robot + */ + public static Pose3d getTargetPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the robot + */ + public static Pose3d getCameraPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiBlue(String limelightName) { + + double[] result = getBotPose_wpiBlue(limelightName); + return toPose2D(result); + } + + /** + * Gets the MegaTag1 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpiblue", false); + } + + /** + * Gets the MegaTag2 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * Make sure you are calling setRobotOrientation() before calling this method. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpiblue", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiRed(String limelightName) { + + double[] result = getBotPose_wpiRed(limelightName); + return toPose2D(result); + + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpired", false); + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpired", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d(String limelightName) { + + double[] result = getBotPose(limelightName); + return toPose2D(result); + + } + + /** + * Gets the current IMU data from NetworkTables. + * IMU data is formatted as [robotYaw, Roll, Pitch, Yaw, gyroX, gyroY, gyroZ, accelX, accelY, accelZ]. + * Returns all zeros if data is invalid or unavailable. + * + * @param limelightName Name/identifier of the Limelight + * @return IMUData object containing all current IMU data + */ + public static IMUData getIMUData(String limelightName) { + double[] imuData = getLimelightNTDoubleArray(limelightName, "imu"); + if (imuData == null || imuData.length < 10) { + return new IMUData(); // Returns object with all zeros + } + return new IMUData(imuData); + } + + ///// + ///// + + public static void setPipelineIndex(String limelightName, int pipelineIndex) { + setLimelightNTDouble(limelightName, "pipeline", pipelineIndex); + } + + + public static void setPriorityTagID(String limelightName, int ID) { + setLimelightNTDouble(limelightName, "priorityid", ID); + } + + /** + * Sets LED mode to be controlled by the current pipeline. + * @param limelightName Name of the Limelight camera + */ + public static void setLEDMode_PipelineControl(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 0); + } + + public static void setLEDMode_ForceOff(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 1); + } + + public static void setLEDMode_ForceBlink(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 2); + } + + public static void setLEDMode_ForceOn(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 3); + } + + /** + * Enables standard side-by-side stream mode. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_Standard(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 0); + } + + /** + * Enables Picture-in-Picture mode with secondary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPMain(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 1); + } + + /** + * Enables Picture-in-Picture mode with primary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPSecondary(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 2); + } + + + /** + * Sets the crop window for the camera. The crop window in the UI must be completely open. + * @param limelightName Name of the Limelight camera + * @param cropXMin Minimum X value (-1 to 1) + * @param cropXMax Maximum X value (-1 to 1) + * @param cropYMin Minimum Y value (-1 to 1) + * @param cropYMax Maximum Y value (-1 to 1) + */ + public static void setCropWindow(String limelightName, double cropXMin, double cropXMax, double cropYMin, double cropYMax) { + double[] entries = new double[4]; + entries[0] = cropXMin; + entries[1] = cropXMax; + entries[2] = cropYMin; + entries[3] = cropYMax; + setLimelightNTDoubleArray(limelightName, "crop", entries); + } + + /** + * Sets 3D offset point for easy 3D targeting. + */ + public static void setFiducial3DOffset(String limelightName, double offsetX, double offsetY, double offsetZ) { + double[] entries = new double[3]; + entries[0] = offsetX; + entries[1] = offsetY; + entries[2] = offsetZ; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Sets robot orientation values used by MegaTag2 localization algorithm. + * + * @param limelightName Name/identifier of the Limelight + * @param yaw Robot yaw in degrees. 0 = robot facing red alliance wall in FRC + * @param yawRate (Unnecessary) Angular velocity of robot yaw in degrees per second + * @param pitch (Unnecessary) Robot pitch in degrees + * @param pitchRate (Unnecessary) Angular velocity of robot pitch in degrees per second + * @param roll (Unnecessary) Robot roll in degrees + * @param rollRate (Unnecessary) Angular velocity of robot roll in degrees per second + */ + public static void SetRobotOrientation(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, true); + } + + public static void SetRobotOrientation_NoFlush(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, false); + } + + private static void SetRobotOrientation_INTERNAL(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate, boolean flush) { + + double[] entries = new double[6]; + entries[0] = yaw; + entries[1] = yawRate; + entries[2] = pitch; + entries[3] = pitchRate; + entries[4] = roll; + entries[5] = rollRate; + setLimelightNTDoubleArray(limelightName, "robot_orientation_set", entries); + if(flush) + { + Flush(); + } + } + + /** + * Configures the IMU mode for MegaTag2 Localization + * + * @param limelightName Name/identifier of the Limelight + * @param mode IMU mode. + */ + public static void SetIMUMode(String limelightName, int mode) { + setLimelightNTDouble(limelightName, "imumode_set", mode); + } + + /** + * Sets the 3D point-of-interest offset for the current fiducial pipeline. + * https://docs.limelightvision.io/docs/docs-limelight/pipeline-apriltag/apriltag-3d#point-of-interest-tracking + * + * @param limelightName Name/identifier of the Limelight + * @param x X offset in meters + * @param y Y offset in meters + * @param z Z offset in meters + */ + public static void SetFidcuial3DOffset(String limelightName, double x, double y, + double z) { + + double[] entries = new double[3]; + entries[0] = x; + entries[1] = y; + entries[2] = z; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Overrides the valid AprilTag IDs that will be used for localization. + * Tags not in this list will be ignored for robot pose estimation. + * + * @param limelightName Name/identifier of the Limelight + * @param validIDs Array of valid AprilTag IDs to track + */ + public static void SetFiducialIDFiltersOverride(String limelightName, int[] validIDs) { + double[] validIDsDouble = new double[validIDs.length]; + for (int i = 0; i < validIDs.length; i++) { + validIDsDouble[i] = validIDs[i]; + } + setLimelightNTDoubleArray(limelightName, "fiducial_id_filters_set", validIDsDouble); + } + + /** + * Sets the downscaling factor for AprilTag detection. + * Increasing downscale can improve performance at the cost of potentially reduced detection range. + * + * @param limelightName Name/identifier of the Limelight + * @param downscale Downscale factor. Valid values: 1.0 (no downscale), 1.5, 2.0, 3.0, 4.0. Set to 0 for pipeline control. + */ + public static void SetFiducialDownscalingOverride(String limelightName, float downscale) + { + int d = 0; // pipeline + if (downscale == 1.0) + { + d = 1; + } + if (downscale == 1.5) + { + d = 2; + } + if (downscale == 2) + { + d = 3; + } + if (downscale == 3) + { + d = 4; + } + if (downscale == 4) + { + d = 5; + } + setLimelightNTDouble(limelightName, "fiducial_downscale_set", d); + } + + /** + * Sets the camera pose relative to the robot. + * @param limelightName Name of the Limelight camera + * @param forward Forward offset in meters + * @param side Side offset in meters + * @param up Up offset in meters + * @param roll Roll angle in degrees + * @param pitch Pitch angle in degrees + * @param yaw Yaw angle in degrees + */ + public static void setCameraPose_RobotSpace(String limelightName, double forward, double side, double up, double roll, double pitch, double yaw) { + double[] entries = new double[6]; + entries[0] = forward; + entries[1] = side; + entries[2] = up; + entries[3] = roll; + entries[4] = pitch; + entries[5] = yaw; + setLimelightNTDoubleArray(limelightName, "camerapose_robotspace_set", entries); + } + + ///// + ///// + + public static void setPythonScriptData(String limelightName, double[] outgoingPythonData) { + setLimelightNTDoubleArray(limelightName, "llrobot", outgoingPythonData); + } + + public static double[] getPythonScriptData(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "llpython"); + } + + ///// + ///// + + /** + * Asynchronously take snapshot. + */ + public static CompletableFuture takeSnapshot(String tableName, String snapshotName) { + return CompletableFuture.supplyAsync(() -> { + return SYNCH_TAKESNAPSHOT(tableName, snapshotName); + }); + } + + private static boolean SYNCH_TAKESNAPSHOT(String tableName, String snapshotName) { + URL url = getLimelightURLString(tableName, "capturesnapshot"); + try { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + if (snapshotName != null && snapshotName != "") { + connection.setRequestProperty("snapname", snapshotName); + } + + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + return true; + } else { + System.err.println("Bad LL Request"); + } + } catch (IOException e) { + System.err.println(e.getMessage()); + } + return false; + } + + /** + * Gets the latest JSON results output and returns a LimelightResults object. + * @param limelightName Name of the Limelight camera + * @return LimelightResults object containing all current target data + */ + public static LimelightResults getLatestResults(String limelightName) { + + long start = System.nanoTime(); + LimelightHelpers.LimelightResults results = new LimelightHelpers.LimelightResults(); + if (mapper == null) { + mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + try { + results = mapper.readValue(getJSONDump(limelightName), LimelightResults.class); + } catch (JsonProcessingException e) { + results.error = "lljson error: " + e.getMessage(); + } + + long end = System.nanoTime(); + double millis = (end - start) * .000001; + results.latency_jsonParse = millis; + if (profileJSON) { + System.out.printf("lljson: %.2f\r\n", millis); + } + + return results; + } +} \ No newline at end of file diff --git a/2026-Rebuilt/src/main/java/frc/robot/Robot.java b/2026-Rebuilt/src/main/java/frc/robot/Robot.java index 04e2013..24985be 100644 --- a/2026-Rebuilt/src/main/java/frc/robot/Robot.java +++ b/2026-Rebuilt/src/main/java/frc/robot/Robot.java @@ -4,41 +4,120 @@ package frc.robot; -import edu.wpi.first.util.sendable.SendableRegistry; -import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.TimedRobot; -import edu.wpi.first.wpilibj.drive.DifferentialDrive; -import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; /** - * This is a demo program showing the use of the DifferentialDrive class, specifically it contains - * the code necessary to operate a robot with tank drive. + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. */ public class Robot extends TimedRobot { - private final DifferentialDrive m_robotDrive; - private final Joystick m_leftStick; - private final Joystick m_rightStick; + private Command m_autonomousCommand; - private final PWMSparkMax m_leftMotor = new PWMSparkMax(0); - private final PWMSparkMax m_rightMotor = new PWMSparkMax(1); + private RobotContainer m_robotContainer; - /** Called once at the beginning of the robot program. */ - public Robot() { - // We need to invert one side of the drivetrain so that positive voltages - // result in both sides moving forward. Depending on how your robot's - // gearbox is constructed, you might have to invert the left side instead. - m_rightMotor.setInverted(true); + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + @Override + public void robotInit() { + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); + m_robotContainer.InitSmartDashboard(); + } + + /** + * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics + * that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @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(); + + // Send subsystem and robot state back to the driver station. + m_robotContainer.periodic(); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() { + m_robotContainer.disabledInit(); + } + + @Override + public void disabledPeriodic() { + //LimelightHelpers.SetIMUMode("limelight", 3); + m_robotContainer.disabledPeriodic(); + } + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + m_robotContainer.enabledInit(); + m_robotContainer.autoInit(); + + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); - m_robotDrive = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); - m_leftStick = new Joystick(0); - m_rightStick = new Joystick(1); + // schedule the autonomous command (example) + if (m_autonomousCommand != null) { + CommandScheduler.getInstance().schedule(m_autonomousCommand); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + + m_robotContainer.enabledInit(); - SendableRegistry.addChild(m_robotDrive, m_leftMotor); - SendableRegistry.addChild(m_robotDrive, m_rightMotor); + m_robotContainer.teleopInit(); } + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + @Override - public void teleopPeriodic() { - m_robotDrive.tankDrive(-m_leftStick.getY(), -m_rightStick.getY()); + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + m_robotContainer.testInit(); } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() { + m_robotContainer.testPeriodic(); + } + + /** This function is called once when the robot is first started up. */ + @Override + + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} } diff --git a/2026-Rebuilt/src/main/java/frc/robot/RobotContainer.java b/2026-Rebuilt/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..10638f7 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,632 @@ +// 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 frc.robot; + +import java.util.Optional; +import java.util.function.BooleanSupplier; + +import com.ctre.phoenix6.hardware.TalonFX; +import com.pathplanner.lib.commands.PathfindingCommand; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.util.PathPlannerLogging; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.DriverStation.Alliance; +import edu.wpi.first.wpilibj.smartdashboard.Field2d; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.ParallelCommandGroup; +import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import edu.wpi.first.wpilibj.PneumaticHub; +import frc.robot.factorys.DriveSubsystemFactory; +import frc.robot.factorys.TalonFXFactory; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.subsystems.LauncherSubsystem; +import frc.robot.subsystems.LiftSubsystem; +import frc.robot.subsystems.IndexerBulkSubsystem; +import frc.robot.subsystems.IntakeSubsystem; +import frc.robot.subsystems.ClimbL1Subsystem; +import frc.robot.subsystems.LimelightSubsystem; +import frc.robot.utils.HubUtils; +import frc.robot.utils.Pigeon; +import frc.robot.utils.PneumaticHubWrapper; +import frc.robot.commands.*; +import frc.robot.commands.autoCommands.DeadreckonDistance; +import frc.robot.commands.util.CancelDriveCommand; +import frc.robot.utils.LauncherUtils; +import frc.robot.Constants.*; +import frc.robot.Constants.RobotConstants.kStartingNames; + +/** + * 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 trigger mappings) should be declared here. + */ +public class RobotContainer { + private final Pigeon m_pigeon; + + // The robot's subsystems and commands are defined here... + private final Optional m_driveSubsystem; + private Optional m_intakeSubsystem; + private Optional m_climbL1Subsystem; + private final Optional m_indexerBulkSubsystem; + private final Optional m_launcherSubsystem; + private final Optional m_liftSubsystem; + private final Optional m_pneumaticHub; + private final LimelightSubsystem m_limelightSubsystem; + + // Replace with CommandPS4Controller or CommandJoystick if needed + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + private final CommandXboxController m_operatorController = + new CommandXboxController(OperatorConstants.kOperatorControllerPort); + + private final DifferentialDriveKinematics m_DriveKinematics = new DifferentialDriveKinematics(DrivetrainConstants.kTrackWidthMeters); + + // A Static Standard Deviation, in the form of [x, y, theta]ᵀ in meters and radians. + private Vector m_drivetrainError = VecBuilder.fill(0.2, 0.2, 0); + private Vector m_limelightError = VecBuilder.fill(.7,.7,9999999); // This gets updated per report + + private DifferentialDrivePoseEstimator m_PoseEstimator = new DifferentialDrivePoseEstimator( + m_DriveKinematics, + Rotation2d.fromDegrees(0.0), + 0, + 0, + new Pose2d(0.0, 0.0, new Rotation2d()), + m_drivetrainError, + m_limelightError + ); + + // The general constraints for most paths. + PathConstraints m_constraints = new PathConstraints( + 2.0, + 1.0, + (1/2) * Math.PI, + (1/4) * Math.PI + ); + + // Smartdashboard Objects + private SendableChooser m_autoChooser; + private SendableChooser m_startingChooser; + private final Field2d m_field = new Field2d(); + + // Factorys + private TalonFXFactory m_TalonFXFactory = new TalonFXFactory(); + private DriveSubsystemFactory m_DriveSubsystemFactory = new DriveSubsystemFactory(); + + // Keep track of time for SmartDashboard updates. + static int m_iTickCount = 0; + + // Checks if the robot is on the blue or red alliance. If it cannot get the data it defaults to blue. + public BooleanSupplier m_isRed = () -> { + Optional alliance = DriverStation.getAlliance(); + if (alliance.isPresent()) { + return alliance.get() == DriverStation.Alliance.Red; + } else { + System.out.println("Could not fetch alliance! Defaulting to blue!"); + return false; + } + }; + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + // Init Pigeon + m_pigeon = new Pigeon(RobotConstants.kPigeon2CANID); + + // Init DriveSubsystem + Optional rightLead = m_TalonFXFactory.construct(DrivetrainConstants.kRightMotorCANID); + Optional leftLead = m_TalonFXFactory.construct(DrivetrainConstants.kLeftMotorCANID); + Optional rightFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalRightMotorCANID); + Optional leftFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalLeftMotorCANID); + m_driveSubsystem = m_DriveSubsystemFactory.construct(m_PoseEstimator, m_DriveKinematics, m_pigeon, rightLead, leftLead, rightFollower, leftFollower, m_isRed); + + // Init the subsystems that don't require pneumatics. + m_limelightSubsystem = new LimelightSubsystem(m_PoseEstimator); + m_launcherSubsystem = getSubsystem(LauncherSubsystem.class); + m_indexerBulkSubsystem = getSubsystem(IndexerBulkSubsystem.class); + m_liftSubsystem = getSubsystem(LiftSubsystem.class); + + // Init pneumatics system and subsystems that rely upon it. + m_pneumaticHub = getSubsystem(PneumaticHubWrapper.class); + Boolean bHasPneumatics = m_pneumaticHub.isPresent(); + + if (bHasPneumatics) + { + PneumaticHub hub = m_pneumaticHub.get(); + hub.enableCompressorAnalog(PneumaticsConstants.kMinPneumaticsPressure, + PneumaticsConstants.kMaxPneumaticsPressure); + } + + // Instantiate subsystems that rely upon pneumatics. We want to run + // the constructors even if the pneumatic hub is not present so that + // they can create SmartDashboard objects we want to use later. + try { + ClimbL1Subsystem climb = new ClimbL1Subsystem(bHasPneumatics); + m_climbL1Subsystem = Optional.ofNullable(climb); + } catch (Exception e) { + m_climbL1Subsystem = Optional.empty(); + } + + try { + IntakeSubsystem intake = new IntakeSubsystem(bHasPneumatics); + m_intakeSubsystem = Optional.ofNullable(intake); + } catch (Exception e) { + m_intakeSubsystem = Optional.empty(); + } + + // Init Auto + configureAutos(); + + // Configure the default commands + configureDefaultCommands(); + + // Configure the trigger bindings + configureBindings(); + } + + // Tom wrote this cool template to make the optional subsystem creation code in + // the constructor above a lot clearer. This is what clever coding looks like. + private static Optional getSubsystem(Class subsystemClass) { + Optional iss; + try { + iss = Optional.ofNullable(subsystemClass.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + iss = Optional.empty(); + } + return iss; + } + + // Init Autos (/home/lvuser/deploy/pathplanner/autos) + private void configureAutos() { + if (m_driveSubsystem.isPresent() && m_launcherSubsystem.isPresent() && m_liftSubsystem.isPresent() && m_indexerBulkSubsystem.isPresent()) { + // Init Needed values + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + LauncherSubsystem launcherSubsystem = m_launcherSubsystem.get(); + LiftSubsystem liftSubsystem = m_liftSubsystem.get(); + IndexerBulkSubsystem indexerBulkSubsystem = m_indexerBulkSubsystem.get(); + m_autoChooser = new SendableChooser(); + + // Init Autos + m_autoChooser.setDefaultOption("NONE", new CancelDriveCommand(driveSubsystem)); + m_autoChooser.addOption("[INDEV] Basic backup and shoot", + new ParallelCommandGroup( + new TrackHubFlywheelCommand(launcherSubsystem, m_PoseEstimator, m_isRed), + new SequentialCommandGroup( + new DeadreckonDistance(driveSubsystem, 1.5, -2.0), + new AimAtHub(driveSubsystem, m_PoseEstimator, RobotConstants.kRotateToHubSpeed, m_isRed), + new WaitForFlywheelAtTarget(launcherSubsystem, LauncherConstants.kFlywheelToleranceRPM), + new StartShootCommand(liftSubsystem, indexerBulkSubsystem) + ) + ) + ); + + // Warm up Pathfinder + CommandScheduler.getInstance().schedule( + PathfindingCommand.warmupCommand() + .andThen(new InstantCommand( + () -> m_field.getObject("target").setPose(new Pose2d()) + )) + ); + + // Add to dashboard + SmartDashboard.putData("Auto Chooser", m_autoChooser); + + // Field wigit update + PathPlannerLogging.setLogTargetPoseCallback((pose) -> { + m_field.getObject("target").setPose(pose); + }); + + PathPlannerLogging.setLogActivePathCallback((poses) -> { + m_field.getObject("path").setPoses(poses); + }); + } else { + try { + DriverStation.reportWarning("Drive Subsystem is not present! No Auto's configured.", null); + } catch (Exception e) { + // If using the SimGUI with no drivetrain components on the robot, the prior call throws + // a null pointer exception which is unhelpful. Let's see if this lets us ignore it. + } + + } + } + + private void configureDefaultCommands() { + if (m_driveSubsystem.isPresent()) + { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + driveSubsystem.initDefaultCommand(m_driverController); + } + } + + /** + * Use this method to define your trigger->command mappings. Triggers can be created via the + * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary + * predicate, or via the named factories in {@link + * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link + * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller + * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight + * joysticks}. + */ + private void configureBindings() { + // Set dashboard indicators showing which subsystems are actually present. + SmartDashboard.putBoolean("Drive Subsystem", m_driveSubsystem.isPresent()); + SmartDashboard.putBoolean("Intake Subsystem", m_intakeSubsystem.isPresent()); + SmartDashboard.putBoolean("ClimbL1 Subsystem", m_climbL1Subsystem.isPresent()); + SmartDashboard.putBoolean("Launcher Subsystem", m_launcherSubsystem.isPresent()); + SmartDashboard.putBoolean("Lift Subsystem", m_liftSubsystem.isPresent()); + SmartDashboard.putBoolean("IndexerBulk Subsystem", m_indexerBulkSubsystem.isPresent()); + SmartDashboard.putBoolean("Pneumatic Subsystem", m_pneumaticHub.isPresent()); + + + // Note that some of our command bindings require that multiple subsystems + // are present. We only enable a binding if all its requirements are met! + // The command bindings here are implemented based on the 2026 Robot User Manual + // found at https://docs.google.com/document/d/1VfiFjz9N2ol3pl7xUKzU2Jam5AbL1xXlS_cHFYYanFM + + if (m_driveSubsystem.isPresent()) { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + + // Aim to Hub (TESTING?) + m_driverController.rightBumper().onTrue( + new AimAtHub(driveSubsystem, m_PoseEstimator, RobotConstants.kRotateToHubSpeed, m_isRed) + ); + + // Range launcher, turn to face hub and shoot all fuel + if (m_launcherSubsystem.isPresent()) { + LauncherSubsystem launcher = m_launcherSubsystem.get(); + m_operatorController.y().onTrue(new TrackHubFlywheelCommand(launcher, m_PoseEstimator, m_isRed)); + + if (m_liftSubsystem.isPresent()) { + LiftSubsystem lift = m_liftSubsystem.get(); + + if (m_indexerBulkSubsystem.isPresent()) { + IndexerBulkSubsystem indexer = m_indexerBulkSubsystem.get(); + + m_operatorController.x().onTrue(new StartShootCommand(lift, indexer)); + m_operatorController.b().onTrue(new StopShootCommand(lift, indexer)); + + m_operatorController.a().onTrue( + new StopShootCommand(lift, indexer) + .andThen(new StopLauncherCommand(launcher)) + ); + + m_operatorController.rightStick().whileTrue(new ReverseIndexBulk(indexer)); + m_operatorController.rightStick().onFalse(new StopShootCommand(lift, indexer)); + + + // This is intended to keep shooting until the button is released. + m_driverController.y().whileTrue( + new ParallelCommandGroup( + new TrackHubFlywheelCommand(launcher, m_PoseEstimator, m_isRed), + new SequentialCommandGroup( + new AimAtHub(driveSubsystem, m_PoseEstimator, RobotConstants.kRotateToHubSpeed, m_isRed), + new WaitForFlywheelAtTarget(launcher, LauncherConstants.kFlywheelToleranceRPM), + new StartShootCommand(lift, indexer) + ) + ) + ); + + m_driverController.y().onFalse( + new StopShootCommand(lift, indexer) + .andThen(new StopLauncherCommand(launcher)) + ); + } else { + m_operatorController.x().onTrue(new StartLiftCommand(lift)); + m_operatorController.b().onTrue(new StopLiftCommand(lift)); + } + } else { + m_operatorController.a().onTrue(new StopLauncherCommand(launcher)); + } + } + + // Travel to corner and shoot + // m_driverController.x().onTrue( + // new TravelToCornerAndShoot(driveSubsystem, m_PoseEstimator, m_isRed, m_constraints) + // ); + + // TEST COMMAND FOR TESTING ACCURACY AFTER BUMP! + m_driverController.leftBumper().onTrue( + new RotateToRotation2D(driveSubsystem, m_PoseEstimator, Rotation2d.kZero, 2.0) + ); + + // cancel the current command running on drive subsystem when + // left y or right x is > half and the default command is not running. + new Trigger(() -> (Math.abs(m_driverController.getLeftY()) > 0.5) && (driveSubsystem.getCurrentCommand().getClass() != driveSubsystem.getDefaultCommand().getClass())) + .onTrue(new CancelDriveCommand(driveSubsystem)); + + new Trigger(() -> (Math.abs(m_driverController.getRightX()) > 0.5) && (driveSubsystem.getCurrentCommand().getClass() != driveSubsystem.getDefaultCommand().getClass())) + .onTrue(new CancelDriveCommand(driveSubsystem)); + + // Also do it on back press + m_driverController.back().onTrue(new CancelDriveCommand(driveSubsystem)); + } + + // + // Operator Controls + // + if (m_intakeSubsystem.isPresent()) + { + m_operatorController.rightBumper().onTrue(new StartIntakeCommand(m_intakeSubsystem.get())); + m_operatorController.leftBumper().onTrue(new StopIntakeCommand(m_intakeSubsystem.get())); + m_operatorController.povUp().onTrue(new ExtendIntakeCommand(m_intakeSubsystem.get())); + m_operatorController.povDown().onTrue(new RetractIntakeCommand(m_intakeSubsystem.get())); + } + + if (m_climbL1Subsystem.isPresent()) + { + // Operator's manual climb overrides. + m_operatorController.start().onTrue(new RaiseClimbCommand(m_climbL1Subsystem.get())); + m_operatorController.back().onTrue(new LowerClimbCommand(m_climbL1Subsystem.get())); + } + } + + // Populate the SmartDashboard on robot init. + public void InitSmartDashboard() { + // Non-Subsystem Specific Stuff + SmartDashboard.putData(CommandScheduler.getInstance()); + SmartDashboard.putData("Field", m_field); + + SmartDashboard.putNumber("Target Rotation", 0); + SmartDashboard.putNumber("Target Angle Diff Abs", 0); + SmartDashboard.putNumber("Rotation Speed", 0); + + SmartDashboard.putNumber("Hub Distance", HubUtils.getHubDistance(m_PoseEstimator, m_isRed)); + SmartDashboard.putNumber("mt2 Tag Count", 0.0); + + SmartDashboard.putBoolean("Launcher SafeToShoot", false); + + SmartDashboard.putNumber("Yaw", 0.0); + SmartDashboard.putNumber("Pitch", 0.0); + SmartDashboard.putNumber("Roll", 0.0); + + SmartDashboard.putNumber("Yaw Rate", 0.0); + SmartDashboard.putNumber("Pitch Rate", 0.0); + SmartDashboard.putNumber("Roll Rate", 0.0); + + SmartDashboard.putNumber("Auto Wait Time", 0.0); + + SmartDashboard.putBoolean("Enabled", DriverStation.isEnabled()); + // SmartDashboard.putBoolean("Pidgeon Accurate", m_pigeon.isAccurate()); + + m_startingChooser = new SendableChooser(); + m_startingChooser.setDefaultOption("NONE", null); + for (kStartingNames startingName : kStartingNames.values()) { + m_startingChooser.addOption(startingName.name(), startingName); + } + SmartDashboard.putData("Starting Position", m_startingChooser); + } + + // This function is called every 20mS. + public void periodic() { + UpdateSmartDashboard(); + } + + //private boolean oldPigeonValue = false; + private void UpdateSmartDashboard() { + // Non-subsystem specific stuff + if ((m_iTickCount % DrivetrainConstants.kTicksPerUpdate) == 0) { + Pose2d estimatedPos = m_PoseEstimator.getEstimatedPosition(); + m_field.setRobotPose(estimatedPos); + + SmartDashboard.putNumber("Pose X (Meter)", estimatedPos.getX()); + SmartDashboard.putNumber("Pose Y (Meter)", estimatedPos.getY()); + SmartDashboard.putNumber("Pose Theta (Degrees)", estimatedPos.getRotation().getDegrees()); + + SmartDashboard.putNumber("Yaw", m_pigeon.getYaw()); + SmartDashboard.putNumber("Pitch", m_pigeon.getPitch()); + SmartDashboard.putNumber("Roll", m_pigeon.getRoll()); + + SmartDashboard.putNumber("Yaw Rate", m_pigeon.getYawRate()); + SmartDashboard.putNumber("Pitch Rate", 0.0); + SmartDashboard.putNumber("Roll Rate", 0.0); + + //SmartDashboard.putNumber("Limelight robotYaw", m_limelightSubsystem.getRobotYaw()); + SmartDashboard.putNumber("Hub Distance", HubUtils.getHubDistance(m_PoseEstimator, m_isRed)); + + SmartDashboard.putBoolean("Enabled", DriverStation.isEnabled()); + + // boolean pigeonAccuracy = m_pigeon.isAccurate(); + // boolean dashboardAccuracy = SmartDashboard.getBoolean("Pidgeon Accurate", pigeonAccuracy); + + // if (pigeonAccuracy != dashboardAccuracy) { + // if (pigeonAccuracy == oldPigeonValue) { + // m_pigeon.setAccuracy(dashboardAccuracy); + // pigeonAccuracy = dashboardAccuracy; + // } else { + // SmartDashboard.putBoolean("Pidgeon Accurate", pigeonAccuracy); + // } + // } + // oldPigeonValue = pigeonAccuracy; + } + + // Drive subsystem + if(m_driveSubsystem.isPresent() && (m_iTickCount % DrivetrainConstants.kTicksPerUpdate) == 0) + { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + + SmartDashboard.putNumber("Left Speed (MPS)", driveSubsystem.getMotorSpeedMPS(true)); + SmartDashboard.putNumber("Right Speed (MPS)", driveSubsystem.getMotorSpeedMPS(false)); + SmartDashboard.putNumber("Left Speed (RPS)", driveSubsystem.getMotorSpeedRPS(true)); + SmartDashboard.putNumber("Right Speed (RPS)", driveSubsystem.getMotorSpeedRPS(false)); + + SmartDashboard.putNumber("Left Distance (m)", driveSubsystem.getMotorPositionMeters(true)); + SmartDashboard.putNumber("Right Distance (m)", driveSubsystem.getMotorPositionMeters(false)); + + SmartDashboard.putNumber("Gyro Degrees", m_pigeon.getYaw()); + } + + // Pneumatics compressor + if(m_pneumaticHub.isPresent() && ((m_iTickCount % PneumaticsConstants.kTicksPerUpdate) == 0)) + { + PneumaticHub hub = m_pneumaticHub.get(); + SmartDashboard.putNumber("Pressure", hub.getPressure(0)); + SmartDashboard.putBoolean("Compressor Running", hub.getCompressor()); + } + + // Safe-to-shoot indicator. + if(m_launcherSubsystem.isPresent() && ((m_iTickCount % LauncherConstants.kTicksPerDistanceUpdate) == 0)) + { + Boolean bCanShoot = LauncherUtils.canScoreFromHere(m_PoseEstimator, m_isRed); + SmartDashboard.putBoolean("Launcher SafeToShoot", bCanShoot); + } + + m_iTickCount++; + } + + // Move to auto init for competition code. + public void enabledInit() { + LimelightHelpers.SetIMUMode("limelight", 3); + m_limelightSubsystem.setAllowJumps(false); + } + + public void teleopInit() { + configureDefaultCommands(); + } + + public void autoInit() { + // This causes issues when auto's do not reset odometry!! + // if (m_driveSubsystem.isPresent()) { + // DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + + // m_gyro.reset(); + // driveSubsystem.resetPose(new Pose2d()); + // } + } + + private int disabledTick = 0; + public void disabledInit() { + disabledTick = 0; + LimelightHelpers.SetIMUMode("limelight", 3); + m_limelightSubsystem.setAllowJumps(true); + + // Disable all motors on disabled init + if (m_driveSubsystem.isPresent()) { + DriveSubsystem drive = m_driveSubsystem.get(); + drive.setDifferentialSpeedNoPid(0,0); + } + + if (m_intakeSubsystem.isPresent()) { + IntakeSubsystem intake = m_intakeSubsystem.get(); + intake.stop(); + } + + if (m_indexerBulkSubsystem.isPresent()) { + IndexerBulkSubsystem indexBulk = m_indexerBulkSubsystem.get(); + indexBulk.stopBulkTransfer(); + indexBulk.stopIndexer(); + } + + if (m_liftSubsystem.isPresent()) { + LiftSubsystem lift = m_liftSubsystem.get(); + lift.stopLift(); + } + + if(m_launcherSubsystem.isPresent()) + { + LauncherSubsystem launcher = m_launcherSubsystem.get(); + launcher.setTargetSpeedrpm(0.0); + } + } + + // Gets called every disabled tick. + private boolean oldIsRed; + private kStartingNames oldStartEnum; + public void disabledPeriodic() { + // Reset Pidgeon + if ((disabledTick % 20) == 0) { + Double robotYaw = m_limelightSubsystem.getRobotYaw(); + if (robotYaw != null) { + Rotation2d currentRotation = m_pigeon.getRotation2d(); + if (Math.abs(robotYaw - currentRotation.getDegrees()) > 1) { + m_pigeon.reset(); + m_pigeon.setYawOffset(robotYaw); + } + } + } + + // Check the side + boolean isRed = m_isRed.getAsBoolean(); + kStartingNames startEnum = m_startingChooser.getSelected(); + if (startEnum != null) { + if ((isRed != oldIsRed) || (startEnum != oldStartEnum)) { + oldIsRed = isRed; + oldStartEnum = startEnum; + + Pose2d startingPose = RobotConstants.kStartingPositions.get(startEnum).get(m_isRed.getAsBoolean()); + + m_pigeon.reset(); + m_pigeon.setYawOffset(-startingPose.getRotation().getDegrees()); + + m_limelightSubsystem.resetYawQue(); + + if (m_driveSubsystem.isPresent()) { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + driveSubsystem.resetPose(startingPose); + } else { + m_PoseEstimator.resetPosition( + m_pigeon.getRotation2d(), + 0, 0, + startingPose + ); + } + + System.out.println("Reset starting pose!"); + } + } else if(startEnum != oldStartEnum) { + oldStartEnum = null; + } + + disabledTick += 1; + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + return m_autoChooser.getSelected(); + } + + public void testInit() { + + } + + public void testPeriodic() { + if (m_indexerBulkSubsystem.isPresent()) + { + m_indexerBulkSubsystem.get().testPeriodic(); + } + if (m_climbL1Subsystem.isPresent()) + { + m_climbL1Subsystem.get().testPeriodic(); + } + if (m_intakeSubsystem.isPresent()) + { + m_intakeSubsystem.get().testPeriodic(); + } + if (m_launcherSubsystem.isPresent()) + { + m_launcherSubsystem.get().testPeriodic(); + } + if (m_liftSubsystem.isPresent()) + { + m_liftSubsystem.get().testPeriodic(); + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/AimAtHub.java b/2026-Rebuilt/src/main/java/frc/robot/commands/AimAtHub.java new file mode 100644 index 0000000..66e2fe9 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/AimAtHub.java @@ -0,0 +1,37 @@ +package frc.robot.commands; + +import java.util.function.BooleanSupplier; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.HubUtils; +/** An example command that uses an example subsystem. */ +public class AimAtHub extends RotateToRotation2D { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final BooleanSupplier m_isRed; + + /** + * Creates a new AimAtHub. + * + * @param driveSubsystem The driveSubsystem for the robot. + * @param poseEstimator The poseEstimator of the robot. + * @param maxSpeed The max speed the robot is allowed to go in the rotation. + * @param isRed Does the robot need to point towards the red or blue goal, default is blue. + */ + public AimAtHub(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, double maxSpeed, BooleanSupplier isRed) + { + super(driveSubsystem, poseEstimator, Rotation2d.kZero, maxSpeed); + + m_poseEstimator = poseEstimator; + m_isRed = isRed; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Rotation2d targetRotation = HubUtils.getRobotToHubAngle(m_poseEstimator, m_isRed); + super.setTargetRotation(targetRotation); + super.initialize(); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommand.java new file mode 100644 index 0000000..3b39dec --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommand.java @@ -0,0 +1,86 @@ +// 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 frc.robot.commands.DriveCommands; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.OperatorConstants; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class ArcadeDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double speedInput = -m_driveController.getLeftY(); + double turnInput = m_driveController.getRightX(); + + // Set the deadband + if (Math.abs(speedInput) < OperatorConstants.kDriverControllerDeadband) { + speedInput = 0; + } + if (Math.abs(turnInput) < OperatorConstants.kDriverControllerDeadband) { + turnInput = 0; + } + + turnInput = turnInput / 2.0; + + double leftSpeed = speedInput + turnInput; + double rightSpeed = speedInput - turnInput; + + // Find the maximum possible value of (throttle + turn) along the vector + // that the joystick is pointing, then desaturate the wheel speeds + double greaterInput = Math.max(Math.abs(speedInput), Math.abs(turnInput)); + double lesserInput = Math.min(Math.abs(speedInput), Math.abs(turnInput)); + if (greaterInput == 0.0) { + m_subsystem.setDifferentialSpeeds( + 0, + 0 + ); + } else { + double saturatedInput = (greaterInput + lesserInput) / greaterInput; + leftSpeed /= saturatedInput; + rightSpeed /= saturatedInput; + + m_subsystem.setDifferentialSpeeds( + leftSpeed * DrivetrainConstants.kMaxVelocityMPS, + rightSpeed * DrivetrainConstants.kMaxVelocityMPS + ); + } + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommandNoPID.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommandNoPID.java new file mode 100644 index 0000000..2e4ade9 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeDriveCommandNoPID.java @@ -0,0 +1,77 @@ +// 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 frc.robot.commands.DriveCommands; +import frc.robot.Constants.OperatorConstants; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class ArcadeDriveCommandNoPID extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeDriveCommandNoPID(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double speedInput = -m_driveController.getRightY(); + double turnInput = m_driveController.getLeftX(); + + // Set the deadband + if (Math.abs(speedInput) < OperatorConstants.kDriverControllerDeadband) { + speedInput = 0; + } + if (Math.abs(turnInput) < OperatorConstants.kDriverControllerDeadband) { + turnInput = 0; + } + + double leftSpeed = speedInput + turnInput; + double rightSpeed = speedInput - turnInput; + + // Find the maximum possible value of (throttle + turn) along the vector + // that the joystick is pointing, then desaturate the wheel speeds + double greaterInput = Math.max(Math.abs(speedInput), Math.abs(turnInput)); + double lesserInput = Math.min(Math.abs(speedInput), Math.abs(turnInput)); + if (greaterInput == 0.0) { + m_subsystem.setDifferentialSpeedNoPid(0, 0); + } else { + double saturatedInput = (greaterInput + lesserInput) / greaterInput; + leftSpeed /= saturatedInput; + rightSpeed /= saturatedInput; + + m_subsystem.setDifferentialSpeedNoPid(leftSpeed, rightSpeed); + } + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeFromDashboard.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeFromDashboard.java new file mode 100644 index 0000000..6f5ef66 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/ArcadeFromDashboard.java @@ -0,0 +1,43 @@ +package frc.robot.commands.DriveCommands; + +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.subsystems.DriveSubsystem; + +public class ArcadeFromDashboard extends Command { + private final DriveSubsystem m_subsystem; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeFromDashboard(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + double speed = SmartDashboard.getNumber("DB Arcade Set (MPS)", 0); + m_subsystem.setDifferentialSpeeds( speed, speed ); + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentailDriveFromDashboard.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentailDriveFromDashboard.java new file mode 100644 index 0000000..560a518 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentailDriveFromDashboard.java @@ -0,0 +1,45 @@ +package frc.robot.commands.DriveCommands; + +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.subsystems.DriveSubsystem; + +public class DifferentailDriveFromDashboard extends Command { + private final DriveSubsystem m_subsystem; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public DifferentailDriveFromDashboard(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + double leftSpeed = SmartDashboard.getNumber("DB Left Set (MPS)", 0); + double rightSpeed = SmartDashboard.getNumber("DB Right Set (MPS)", 0); + + m_subsystem.setDifferentialSpeeds( leftSpeed, rightSpeed ); + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentialDriveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentialDriveCommand.java new file mode 100644 index 0000000..992cc3a --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/DifferentialDriveCommand.java @@ -0,0 +1,66 @@ +// 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 frc.robot.commands.DriveCommands; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.OperatorConstants; + +/** An example command that uses an example subsystem. */ +public class DifferentialDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public DifferentialDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double leftSpeed = -m_driveController.getLeftY(); + double rightSpeed = -m_driveController.getRightY(); + + // Set the deadband + if (Math.abs(leftSpeed) < OperatorConstants.kDriverControllerDeadband) { + leftSpeed = 0; + } + if (Math.abs(rightSpeed) < OperatorConstants.kDriverControllerDeadband) { + rightSpeed = 0; + } + + leftSpeed *= DrivetrainConstants.kMaxVelocityMPS; + rightSpeed *= DrivetrainConstants.kMaxVelocityMPS; + + m_subsystem.setDifferentialSpeeds( leftSpeed, rightSpeed ); + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/NoDriveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/NoDriveCommand.java new file mode 100644 index 0000000..b674e34 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/NoDriveCommand.java @@ -0,0 +1,53 @@ +// 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 frc.robot.commands.DriveCommands; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class NoDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + + // Doing this because it needs to have a controller for the drive default command. + @SuppressWarnings("unused") + private CommandXboxController m_driveController; + + /** + * Creates a new NoDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public NoDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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_subsystem.setDifferentialSpeeds( + 0, + 0 + ); + } + + // 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/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/TurnLeftTest.java b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/TurnLeftTest.java new file mode 100644 index 0000000..c5b12c2 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/DriveCommands/TurnLeftTest.java @@ -0,0 +1,47 @@ +package frc.robot.commands.DriveCommands; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +/** + * Turns the robot left at a fixed speed known to cause a stall. + * This command has no way to stop internally and must be cancled via another way. +*/ +public class TurnLeftTest extends Command { + private final DriveSubsystem m_driveSubsystem; + private final double m_stallSpeed = 0.23; + + /** + * Creates a new TurnLeftTest. + * + * @param driveSubsystem The driveSubsystem for the robot. + */ + public TurnLeftTest(DriveSubsystem driveSubsystem) + { + m_driveSubsystem = driveSubsystem; + addRequirements(driveSubsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + System.out.println("Starting TurnLeftTest! This command must be cancled via an outside method!"); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + m_driveSubsystem.setDifferentialSpeedNoPid(-m_stallSpeed, m_stallSpeed); + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + m_driveSubsystem.setDifferentialSpeedNoPid(0, 0); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/ExtendIntakeCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/ExtendIntakeCommand.java new file mode 100644 index 0000000..b8a9173 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/ExtendIntakeCommand.java @@ -0,0 +1,52 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IntakeSubsystem; + +/** + * + * Extend the intake mechanism. + * + **/ +public class ExtendIntakeCommand extends Command { + private final IntakeSubsystem m_subsystem; + + /** + * Creates a new ExtendIntakeCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExtendIntakeCommand(IntakeSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This simple command extends the intake mechanism. + m_subsystem.extend(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/LowerClimbCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/LowerClimbCommand.java new file mode 100644 index 0000000..da61a32 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/LowerClimbCommand.java @@ -0,0 +1,51 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.ClimbL1Subsystem; + +/** + * + * Lower the climb L1 mechanism hooks. + * + **/ +public class LowerClimbCommand extends Command { + private final ClimbL1Subsystem m_subsystem; + + /** + * Creates a new LowerClimbCommand. + * + * @param subsystem The subsystem used by this command. + */ + public LowerClimbCommand(ClimbL1Subsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.lowerArms(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/RaiseClimbCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/RaiseClimbCommand.java new file mode 100644 index 0000000..a5efa76 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/RaiseClimbCommand.java @@ -0,0 +1,51 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.ClimbL1Subsystem; + +/** + * + * Raise the climb L1 mechanism hooks. + * + **/ +public class RaiseClimbCommand extends Command { + private final ClimbL1Subsystem m_subsystem; + + /** + * Creates a new RaiseClimbCommand. + * + * @param subsystem The subsystem used by this command. + */ + public RaiseClimbCommand(ClimbL1Subsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.raiseArms(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/RangeLauncherCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/RangeLauncherCommand.java new file mode 100644 index 0000000..3a1b8df --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/RangeLauncherCommand.java @@ -0,0 +1,75 @@ +package frc.robot.commands; + +import java.util.function.BooleanSupplier; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LauncherSubsystem; +import frc.robot.utils.HubUtils; +import frc.robot.utils.LauncherUtils; + + +/** + * + * Stop the launcher motor and spin down the flywheel. + * + **/ +public class RangeLauncherCommand extends Command { + private final BooleanSupplier m_isRed; + private final LauncherSubsystem m_subsystem; + private double m_tolerancerpm; + private double m_targetSpeedrpm; + private final DifferentialDrivePoseEstimator m_poseEstimator; + + /** + * Creates a new RangeLauncherCommand. + * + * @param subsystem The subsystem used by this command. + */ + public RangeLauncherCommand(LauncherSubsystem subsystem, DifferentialDrivePoseEstimator poseEstimator, double Tolerancerpm, BooleanSupplier isRed) + { + m_subsystem = subsystem; + m_tolerancerpm = Tolerancerpm; + m_poseEstimator = poseEstimator; + m_isRed = isRed; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + double distance = HubUtils.getHubDistance(m_poseEstimator, m_isRed); + m_targetSpeedrpm = LauncherUtils.getFlywheelSpeed(distance); + + m_subsystem.setTargetSpeedrpm(m_targetSpeedrpm); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end when the flywheel speed is within the tolerance of the target. + double currentSpeed = m_subsystem.getCurrentSpeedrpm(); + if (Math.abs(currentSpeed - m_targetSpeedrpm) <= m_tolerancerpm) + { + return true; + } + else + { + return false; + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/RetractIntakeCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/RetractIntakeCommand.java new file mode 100644 index 0000000..65c3f1b --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/RetractIntakeCommand.java @@ -0,0 +1,52 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IntakeSubsystem; + +/** + * + * Retract the intake mechanism. + * + **/ +public class RetractIntakeCommand extends Command { + private final IntakeSubsystem m_subsystem; + + /** + * Creates a new RetractIntakeCommand. + * + * @param subsystem The subsystem used by this command. + */ + public RetractIntakeCommand(IntakeSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This simple command retracts the intake mechanism. + m_subsystem.retract(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/ReverseIndexBulk.java b/2026-Rebuilt/src/main/java/frc/robot/commands/ReverseIndexBulk.java new file mode 100644 index 0000000..e1e22de --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/ReverseIndexBulk.java @@ -0,0 +1,50 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IndexerBulkSubsystem; + +/** + * + * Start shooting fuel by starting the lift motor and both motors in the indexer/bulk subsystem. + * + **/ +public class ReverseIndexBulk extends Command { + private final IndexerBulkSubsystem m_IndexerBulk; + + /** + * Creates a new ReverseIndexBulk. + * + * @param subsystem The subsystem used by this command. + */ + public ReverseIndexBulk(IndexerBulkSubsystem indexerbulk) + { + m_IndexerBulk = indexerbulk; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_IndexerBulk); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_IndexerBulk.reverseBulkTransfer(); + m_IndexerBulk.reverseIndexer(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + } + + // This command only ends when another command is scheduled on the + // subsystem. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/RotateToRotation2D.java b/2026-Rebuilt/src/main/java/frc/robot/commands/RotateToRotation2D.java new file mode 100644 index 0000000..22addc3 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/RotateToRotation2D.java @@ -0,0 +1,97 @@ +package frc.robot.commands; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class RotateToRotation2D extends Command { + private final DriveSubsystem m_driveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final double m_maxSpeed; + private Rotation2d m_targetRotation; + + // Bounds are [0, 1) in interval notation. This effects the steepness of + // the curve that is used to slow the robot as it approches the target. + private double speedExp = 0.99; + + /** + * Creates a new RotateToRotation2D. + * + * @param driveSubsystem The driveSubsystem for the robot. + * @param poseEstimator The poseEstimator of the robot. + * @param targetRotation The rotation you want the robot to go to. + * @param maxSpeed The max speed the robot is allowed to go in the rotation. + */ + public RotateToRotation2D(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, Rotation2d targetRotation, double maxSpeed) + { + m_driveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_targetRotation = targetRotation; + m_maxSpeed = maxSpeed; + + SmartDashboard.putNumber("Target Rotation", m_targetRotation.getDegrees()); + + addRequirements(driveSubsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + System.out.println("Starting rotation to: " + m_targetRotation.getDegrees()); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + Rotation2d currentRotation = m_poseEstimator.getEstimatedPosition().getRotation(); + Rotation2d changeInRotation = m_targetRotation.minus(currentRotation); + + double targetDiff = Math.abs(changeInRotation.getDegrees()); + + double speed = ((Math.pow(speedExp, targetDiff)-1)/(Math.pow(speedExp, 180)-1)); + speed *= m_maxSpeed; + speed = Math.max(speed, 0.3); + SmartDashboard.putNumber("Rotation Speed", speed); + + if (changeInRotation.getDegrees() <= 0) { + m_driveSubsystem.setDifferentialSpeeds(speed, -speed); + } else { + m_driveSubsystem.setDifferentialSpeeds(-speed, speed); + } + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + if (interrupted) { + System.out.println("Rotation Interrupted!"); + } else { + System.out.println("Rotatation Complete!"); + } + + m_driveSubsystem.setDifferentialSpeeds(0, 0); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + Rotation2d currentRotation = m_poseEstimator.getEstimatedPosition().getRotation(); + Rotation2d diff = m_targetRotation.minus(currentRotation); + + double currentDiffAbs = Math.abs(diff.getDegrees()); + SmartDashboard.putNumber("Target Angle Diff Abs", currentDiffAbs); + + if (currentDiffAbs <= 0.5) { + return true; + } else { + return false; + } + } + + protected void setTargetRotation(Rotation2d newRotation) { + m_targetRotation = newRotation; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StartBulkMoveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StartBulkMoveCommand.java new file mode 100644 index 0000000..298c353 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StartBulkMoveCommand.java @@ -0,0 +1,49 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IndexerBulkSubsystem; + +/** + * + * Start bulk and indexer motors. + * + **/ +public class StartBulkMoveCommand extends Command { + private final IndexerBulkSubsystem m_subsystem; + + /** + * Creates a new StartBulkMoveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StartBulkMoveCommand(IndexerBulkSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.startBulkTransfer(); + m_subsystem.startIndexer(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StartFlywheelCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StartFlywheelCommand.java new file mode 100644 index 0000000..e80559e --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StartFlywheelCommand.java @@ -0,0 +1,63 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LauncherSubsystem; + +/** + * + * Start the launcher flywheel and get it up to the target speed. + * + **/ +public class StartFlywheelCommand extends Command { + private final LauncherSubsystem m_subsystem; + private final double m_targetSpeedrpm; + private final double m_Tolerancerpm; + + /** + * Creates a new StartFlywheelCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StartFlywheelCommand(LauncherSubsystem subsystem, double TargetSpeedrpm, double Tolerancerpm) + { + m_subsystem = subsystem; + m_targetSpeedrpm = TargetSpeedrpm; + m_Tolerancerpm = Tolerancerpm; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.setTargetSpeedrpm(m_targetSpeedrpm); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end when the flywheel speed is within the tolerance of the target. + double currentSpeed = m_subsystem.getCurrentSpeedrpm(); + if (Math.abs(currentSpeed - m_targetSpeedrpm) <= m_Tolerancerpm) + { + return true; + } + else + { + return false; + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StartIntakeCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StartIntakeCommand.java new file mode 100644 index 0000000..9f233e1 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StartIntakeCommand.java @@ -0,0 +1,52 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IntakeSubsystem; + +/** + * + * Start the intake mechanism roller motor. + * + **/ +public class StartIntakeCommand extends Command { + private final IntakeSubsystem m_subsystem; + + /** + * Creates a new StartIntakeCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StartIntakeCommand(IntakeSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This simple command turns on the intake motor at a fixed speed. + m_subsystem.start(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StartLiftCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StartLiftCommand.java new file mode 100644 index 0000000..f439a66 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StartLiftCommand.java @@ -0,0 +1,48 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LiftSubsystem; + +/** + * + * Start the launcher lift motor. + * + **/ +public class StartLiftCommand extends Command { + private final LiftSubsystem m_subsystem; + + /** + * Creates a new StartLiftCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StartLiftCommand(LiftSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.startLift(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StartShootCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StartShootCommand.java new file mode 100644 index 0000000..b84be80 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StartShootCommand.java @@ -0,0 +1,55 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IndexerBulkSubsystem; +import frc.robot.subsystems.LiftSubsystem; + +/** + * + * Start shooting fuel by starting the lift motor and both motors in the indexer/bulk subsystem. + * + **/ +public class StartShootCommand extends Command { + private final LiftSubsystem m_Lift; + private final IndexerBulkSubsystem m_IndexerBulk; + + /** + * Creates a new StartShootCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StartShootCommand(LiftSubsystem lift, IndexerBulkSubsystem indexerbulk) + { + m_Lift = lift; + m_IndexerBulk = indexerbulk; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_Lift); + addRequirements(m_IndexerBulk); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_IndexerBulk.startBulkTransfer(); + m_IndexerBulk.startIndexer(); + m_Lift.startLift(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + } + + // This command only ends when another command is scheduled on the + // subsystem. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StopBulkMoveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StopBulkMoveCommand.java new file mode 100644 index 0000000..e4e04fa --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StopBulkMoveCommand.java @@ -0,0 +1,49 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IndexerBulkSubsystem; + +/** + * + * Stop bulk move and indexer motors. + * + **/ +public class StopBulkMoveCommand extends Command { + private final IndexerBulkSubsystem m_subsystem; + + /** + * Creates a new StopBulkMoveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StopBulkMoveCommand(IndexerBulkSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.stopBulkTransfer(); + m_subsystem.stopIndexer(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StopIntakeCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StopIntakeCommand.java new file mode 100644 index 0000000..7213f0c --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StopIntakeCommand.java @@ -0,0 +1,52 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IntakeSubsystem; + +/** + * + * Stop the intake mechanism roller motor. + * + **/ +public class StopIntakeCommand extends Command { + private final IntakeSubsystem m_subsystem; + + /** + * Creates a new StopIntakeCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StopIntakeCommand(IntakeSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This simple command turns off the intake motor. + m_subsystem.stop(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StopLauncherCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StopLauncherCommand.java new file mode 100644 index 0000000..0e04e34 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StopLauncherCommand.java @@ -0,0 +1,52 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LiftSubsystem; +import frc.robot.subsystems.LauncherSubsystem; + +/** + * + * Stop the launcher lift motor and spin down the flywheel. + * + **/ +public class StopLauncherCommand extends Command { + private final LauncherSubsystem m_launcher; + + /** + * Creates a new StopLauncherCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StopLauncherCommand(LauncherSubsystem launcher) + { + m_launcher = launcher; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_launcher); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_launcher.setTargetSpeedrpm(0.0); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StopLiftCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StopLiftCommand.java new file mode 100644 index 0000000..8a1b875 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StopLiftCommand.java @@ -0,0 +1,48 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LiftSubsystem; + +/** + * + * Stop the launcher lift motor and spin down the flywheel. + * + **/ +public class StopLiftCommand extends Command { + private final LiftSubsystem m_subsystem; + + /** + * Creates a new StopLiftCommand. + * + * @param subsystem The subsystem used by this command. + */ + public StopLiftCommand(LiftSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_subsystem.stopLift(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/StopShootCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/StopShootCommand.java new file mode 100644 index 0000000..610d807 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/StopShootCommand.java @@ -0,0 +1,54 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.IndexerBulkSubsystem; +import frc.robot.subsystems.LiftSubsystem; + +/** + * + * Start shooting fuel by starting the launcher lift motor and both motors in the indexer/bulk subsystem. + * + **/ +public class StopShootCommand extends Command { + private final LiftSubsystem m_Lift; + private final IndexerBulkSubsystem m_IndexerBulk; + + /** + * Stops shooting by stopping the lift motor and both motors in bulk/indexer. + * + * @param subsystem The subsystem used by this command. + */ + public StopShootCommand(LiftSubsystem lift, IndexerBulkSubsystem indexerbulk) + { + m_Lift = lift; + m_IndexerBulk = indexerbulk; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_Lift); + addRequirements(m_IndexerBulk); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_IndexerBulk.stopBulkTransfer(); + m_IndexerBulk.stopIndexer(); + m_Lift.stopLift(); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + } + + // This command ends immediately. + @Override + public boolean isFinished() { + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/TrackHubFlywheelCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/TrackHubFlywheelCommand.java new file mode 100644 index 0000000..73d5a28 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/TrackHubFlywheelCommand.java @@ -0,0 +1,77 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LauncherSubsystem; + +import java.util.function.BooleanSupplier; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import frc.robot.utils.HubUtils; +import frc.robot.utils.LauncherUtils; + +/** + * + * This command, which runs until interrupted, constantly monitors the + * distance between the robot and the hub and updates the launcher + * flywheel target speed to the correct value for the current position. + * The speed is only modified if the distance is within the range where + * it is physically possible for the robot to successfully launch fuel + * into the hub. + * + **/ +public class TrackHubFlywheelCommand extends Command { + private final LauncherSubsystem m_subsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final BooleanSupplier m_isRed; + private double m_targetSpeedrpm = 0.0; + + /** + * Creates a new TrackHubFlywheelCommand. + * + * @param subsystem The subsystem used by this command. + */ + public TrackHubFlywheelCommand(LauncherSubsystem subsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed) + { + m_subsystem = subsystem; + m_poseEstimator = poseEstimator; + m_isRed = isRed; + m_targetSpeedrpm = 0.0; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + + // Calculate the current distance to the hub. + double Distance = HubUtils.getHubDistance(m_poseEstimator, m_isRed); + + // Determine flywheel speed to target hub from this distance. + m_targetSpeedrpm = LauncherUtils.getFlywheelSpeed(Distance); + + // Set new flywheel target speed. + m_subsystem.setTargetSpeedrpm(m_targetSpeedrpm); + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + + } + + // This command never ends. It will be interrupted by the scheduler if + // the operator chooses to run another command that requires the launcher + // subsystem. + @Override + public boolean isFinished() { + return false; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/WaitCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/WaitCommand.java new file mode 100644 index 0000000..ac64df9 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/WaitCommand.java @@ -0,0 +1,45 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +/** An example command that uses an example subsystem. */ +public class WaitCommand extends Command { + private final double m_totalCalls; + private double m_callCount; + + /** + * Creates a new WaitCommand. + */ + public WaitCommand(double seconds) + { + m_totalCalls = seconds * 50; // 50 calls per second. + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + System.out.println("Starting timer for " + String.valueOf(m_totalCalls)); + m_callCount = 0; + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + m_callCount++; + System.out.println("Timer is now at call count " + String.valueOf(m_callCount)); + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + System.out.println("Timer is now finished!"); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + boolean check = m_callCount >= m_totalCalls; + System.out.println(String.valueOf(m_callCount) + " >= " + String.valueOf(m_totalCalls) + ": " + String.valueOf(check)); + + return check; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/WaitForFlywheelAtTarget.java b/2026-Rebuilt/src/main/java/frc/robot/commands/WaitForFlywheelAtTarget.java new file mode 100644 index 0000000..449373c --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/WaitForFlywheelAtTarget.java @@ -0,0 +1,50 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.LauncherSubsystem; + +/** + * + * Start the launcher flywheel and get it up to the target speed. + * + **/ +public class WaitForFlywheelAtTarget extends Command { + private final LauncherSubsystem m_subsystem; + private final double m_toleranceRPM; + + /** + * Creates a new WaitForFlywheelAtTarget. + * + * @param subsystem The subsystem used by this command. + */ + public WaitForFlywheelAtTarget(LauncherSubsystem subsystem, double toleranceRPM) + { + m_subsystem = subsystem; + m_toleranceRPM = toleranceRPM; + } + + // 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() {} + + // 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() { + // The command signals end when the flywheel speed is within the tolerance of the target. + double targetSpeed = m_subsystem.getTargetSpeedrpm(); + double currentSpeed = m_subsystem.getCurrentSpeedrpm(); + if (Math.abs(currentSpeed - targetSpeed) <= m_toleranceRPM) { + return true; + } else { + return false; + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndClimb.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndClimb.java new file mode 100644 index 0000000..18f542c --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndClimb.java @@ -0,0 +1,98 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.commands.WaitCommand; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class BackupAndClimb extends Command { + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "LineupToTower" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DSA_BackupTest. + */ + public BackupAndClimb(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Rotation2d targetRot; + if (m_isRed.getAsBoolean() == true) { + targetRot = Rotation2d.kZero; + } else { + targetRot = Rotation2d.k180deg; + } + + // Init the new sequence + ArrayList sequence = new ArrayList(Arrays.asList( + new DeadreckonDistance(m_DriveSubsystem, 1.0, -1), + "LineupToTower", + new RotateToRotation2D(m_DriveSubsystem, m_PoseEstimator, targetRot, 1.0), + new WaitCommand(0.2), + new DeadreckonDistance(m_DriveSubsystem, 0.5, 1.0) + )); + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.GO_TO_FIRST_PATH, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndShootThenClimb.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndShootThenClimb.java new file mode 100644 index 0000000..31a3bcd --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupAndShootThenClimb.java @@ -0,0 +1,99 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.commands.WaitCommand; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class BackupAndShootThenClimb extends Command { + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "LineupToTower" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DSA_BackupTest. + */ + public BackupAndShootThenClimb(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Rotation2d targetRot; + if (m_isRed.getAsBoolean() == true) { + targetRot = Rotation2d.kZero; + } else { + targetRot = Rotation2d.k180deg; + } + + // Init the new sequence + ArrayList sequence = new ArrayList(Arrays.asList( + new DeadreckonDistance(m_DriveSubsystem, 1.5, -2.0), + new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 4.0, m_isRed), + new WaitCommand(2.0), + "LineupToTower", + new RotateToRotation2D(m_DriveSubsystem, m_PoseEstimator, targetRot, 1.0) + )); + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.REPLACE_FIRST_PATH, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupCollectDepoAndShoot.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupCollectDepoAndShoot.java new file mode 100644 index 0000000..f0969b1 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/BackupCollectDepoAndShoot.java @@ -0,0 +1,87 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class BackupCollectDepoAndShoot extends Command { + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "GatherDepot" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DSA_BackupTest. + */ + public BackupCollectDepoAndShoot(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // Init the new sequence + ArrayList sequence = new ArrayList(Arrays.asList( + new DeadreckonDistance(m_DriveSubsystem, 0.5, -1.0), + "GatherDepot", + new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 3.0, m_isRed) + )); + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.GO_TO_FIRST_PATH, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/CorralAndShoot.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/CorralAndShoot.java new file mode 100644 index 0000000..c9efc48 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/CorralAndShoot.java @@ -0,0 +1,89 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class CorralAndShoot extends Command { + // The sequance of the command, any "*" are place holders for commands. + private final ArrayList m_baseSequence = new ArrayList(Arrays.asList( + "GatherCorralShootVel", + "*" // Aim at hub + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new CorralAndShoot. + */ + public CorralAndShoot(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_baseSequence) { + if (item instanceof String && (String) item != "*") { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // Init the new sequence + + // !! DO NOT DO THIS!! LOOK AT "DepoSideAutoA.java" FOR A BETTER REFRENCE !! + ArrayList sequence = new ArrayList(m_baseSequence); + sequence.set(1, new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 2.0, m_isRed)); + // !! DO NOT DO THIS!! LOOK AT "DepoSideAutoA.java" FOR A BETTER REFRENCE !! + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.GO_TO_FIRST_PATH, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonBumpAndBack.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonBumpAndBack.java new file mode 100644 index 0000000..2a9e443 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonBumpAndBack.java @@ -0,0 +1,125 @@ +package frc.robot.commands.autoCommands; + +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc.robot.commands.AimAtHub; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.PathUtils; + +/** An example command that uses an example subsystem. */ +public class DeadreckonBumpAndBack extends Command { + private final DriveSubsystem m_driveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final PathConstraints m_constraints; + private final BooleanSupplier m_isRed; + + /** + * Creates a new DeadreckonBumpAndBack. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + */ + public DeadreckonBumpAndBack(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, PathConstraints constraints, BooleanSupplier isRed) + { + m_driveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_constraints = constraints; + m_isRed = isRed; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Double pickupTargetX; + Double lineupTargetX; + if (m_isRed.getAsBoolean() == true) { // Red + pickupTargetX = 9.1; + lineupTargetX = 10.88; + } else { // Blue + pickupTargetX = 7.689; + lineupTargetX = 5.667; + } + + // Deadreckon Over the bump + Command deadreckonFoward = new DeadreckonDistance(m_driveSubsystem, 2.5, 2.0); + Command deadreckonBackward = new DeadreckonDistance(m_driveSubsystem, 2.5, -2.0); + + // Create paths and scheudle other commands + Command lineupSchedule = new InstantCommand(() -> { + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + Rotation2d currentRotation = currentPose.getRotation(); + Double currentY = currentPose.getY(); + + Pose2d lineupTarget = new Pose2d(lineupTargetX, currentY, currentRotation); + + System.out.println(lineupTarget.toString()); + + // Go to the lineup target (backward) + PathPlannerPath lineupPath = PathUtils.OnTheFlyToTarget( + currentPose, + lineupTarget, + m_constraints, + true + ); + Command lineupCommand = AutoBuilder.followPath(lineupPath); + + // Create & Schedule the command group. + CommandScheduler.getInstance().schedule( + lineupCommand + .andThen(deadreckonBackward) + .andThen(new AimAtHub(m_driveSubsystem, m_poseEstimator, 2.0, m_isRed)) + .andThen(new DeadreckonDistance(m_driveSubsystem, 0.75, -1.0)) + .andThen(new AimAtHub(m_driveSubsystem, m_poseEstimator, 2.0, m_isRed)) + ); + }); + + Command pickupScheudle = new InstantCommand(() -> { + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + Rotation2d currentRotation = currentPose.getRotation(); + Double currentY = currentPose.getY(); + + Pose2d pickupTarget = new Pose2d(pickupTargetX, currentY, currentRotation); + + System.out.println(pickupTarget.toString()); + + // Go to the pickup target (forward) + PathPlannerPath pickupPath = PathUtils.OnTheFlyToTarget(currentPose, pickupTarget, m_constraints); + Command pickupCommand = AutoBuilder.followPath(pickupPath); + + // Create & Scheudle the command group + CommandScheduler.getInstance().schedule(pickupCommand.andThen(lineupSchedule)); + }); + + // Create & Schedule the command group. + CommandScheduler.getInstance().schedule( + deadreckonFoward + .andThen(pickupScheudle) + ); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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() { + // The command automatically ends + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonDistance.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonDistance.java new file mode 100644 index 0000000..00bc6a1 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DeadreckonDistance.java @@ -0,0 +1,66 @@ +package frc.robot.commands.autoCommands; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class DeadreckonDistance extends Command { + private final DriveSubsystem m_driveSubsystem; + private double m_targetDistance; + private double m_speed; + + private double m_startDistance; + + /** + * Creates a new DeadreckonDistance. + * To reverse please input a negitive speed value but keep targetDistance positive. + * + * @param subsystem The subsystem used by this command. + */ + public DeadreckonDistance(DriveSubsystem driveSubsystem, double targetDistance, double speed) + { + m_driveSubsystem = driveSubsystem; + m_targetDistance = targetDistance; + m_speed = speed; + + addRequirements(m_driveSubsystem); + } + + private double avrgDistances() { + double leftPos = m_driveSubsystem.getMotorPositionMeters(true); + double rightPos = m_driveSubsystem.getMotorPositionMeters(false); + return (leftPos + rightPos)/2; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + m_startDistance = avrgDistances(); + m_driveSubsystem.setDifferentialSpeeds(m_speed, m_speed); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + m_driveSubsystem.setDifferentialSpeeds(m_speed, m_speed); + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + m_driveSubsystem.setDifferentialSpeeds(0, 0); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + double distanceDiff = Math.abs(avrgDistances() - m_startDistance); + // System.out.println(String.valueOf(distanceDiff) + " --> " + String.valueOf(m_targetDistance) + " at " + String.valueOf(m_speed)); + + if (distanceDiff >= m_targetDistance) { + return true; + } else { + return false; + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoAndShootThenClimb.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoAndShootThenClimb.java new file mode 100644 index 0000000..0433192 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoAndShootThenClimb.java @@ -0,0 +1,108 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.Commands; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.commands.WaitCommand; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class DepoAndShootThenClimb extends Command { + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "GatherDepot", + "FromDepoTowerLineup" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DSA_BackupTest. + */ + public DepoAndShootThenClimb(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Rotation2d targetRot; + if (m_isRed.getAsBoolean() == true) { + targetRot = Rotation2d.kZero; + } else { + targetRot = Rotation2d.k180deg; + } + + // Init the new sequence + ArrayList sequence2 = new ArrayList(Arrays.asList( + "FromDepoTowerLineup", + new RotateToRotation2D(m_DriveSubsystem, m_PoseEstimator, targetRot, 1.0), + new WaitCommand(0.2), + new DeadreckonDistance(m_DriveSubsystem, 0.5, 1.0) + )); + Command auto2 = new AutoFromList(sequence2, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.REPLACE_FIRST_PATH, false); + + ArrayList sequence1 = new ArrayList(Arrays.asList( + new DeadreckonDistance(m_DriveSubsystem, 0.5, -1.0), + "GatherDepot", + new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 3.0, m_isRed), + new WaitCommand(2.0), + Commands.runOnce(() -> {CommandScheduler.getInstance().schedule(auto2);}) + )); + Command auto1 = new AutoFromList(sequence1, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.GO_TO_FIRST_PATH, false); + + CommandScheduler.getInstance().schedule(auto1); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoSideAutoA.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoSideAutoA.java new file mode 100644 index 0000000..d4916fa --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/DepoSideAutoA.java @@ -0,0 +1,138 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.ExtendIntakeCommand; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.commands.StartIntakeCommand; +import frc.robot.commands.StartLiftCommand; +import frc.robot.commands.StopIntakeCommand; +import frc.robot.commands.StopLiftCommand; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class DepoSideAutoA extends Command { + // The pathplanner paths that get pre-loaded for quicker load. + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "DSA_PickupFwd", + "DSA_PickupBwd", + "DSA_ParkAtWall", + "DSA_A_Bump" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DepoSideAutoA. + */ + public DepoSideAutoA(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // Get target rotation + Rotation2d targetRotation; + if (m_isRed.getAsBoolean() == true) { + targetRotation = new Rotation2d(0); // on red, aim at blue wall + } else { + targetRotation = new Rotation2d(180); // on blue, aim at red wall + } + + // Init the new sequence + ArrayList sequence = new ArrayList(Arrays.asList( + new ExtendIntakeCommand(null), // Extend Intake + new InstantCommand(), // Drive Over bump fwd + new InstantCommand(), // Relocate + + // Start Pickup \\ + new StartIntakeCommand(null), // Trigger pickup + new StartLiftCommand(null), // Trigger Lift + "DSA_PickupFwd", // ⚠️⚠️ Might have an issue with location being to far from this path ⚠️⚠️ + + // End Pickup \\ + new StopIntakeCommand(null), // Disable Pickup + new StopLiftCommand(null), // Disable Lift + + // Ready shoot & travel \\ + new RotateToRotation2D(m_DriveSubsystem, m_PoseEstimator, targetRotation, 1), // Aim at other wall + "DSA_PickupBwd", + new InstantCommand(), // Spin-up flywheel?? (Might have better location.) + new InstantCommand(), // Drive over bump bwd + new InstantCommand(), // Relocate + "DSA_ParkAtWall", // ⚠️⚠️ Might have an issue with location being to far from this path ⚠️⚠️ + new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 2.0, m_isRed), // Aim at hub + + // Shoot all balls \\ + new InstantCommand(), // Full engage flywheel or wtv + new StartLiftCommand(null), + + // Ramp down flywheel \\ + new StopLiftCommand(null), + new InstantCommand(), // Disable flywheel + + // Get ready to pickup when auto ends \\ + "DSA_A_Bump", + new InstantCommand(), // Drive over bump fwd + new InstantCommand() // Relocate + )); + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.NONE, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OnTheFlyToTarget.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OnTheFlyToTarget.java new file mode 100644 index 0000000..4289dbd --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OnTheFlyToTarget.java @@ -0,0 +1,86 @@ +package frc.robot.commands.autoCommands; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.utils.PathUtils; + +public class OnTheFlyToTarget extends Command { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private Pose2d m_targetPose; + private final PathConstraints m_constraints; + private final Boolean m_reversed; + private final double m_endVelocity; + + /** + * This creates and schedules a path on the fly to a given target point. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param endVelocity The velocity it should be at the end of the path. + * @param reversed If the robot should run the path in reversed (backward) + */ + public OnTheFlyToTarget(DifferentialDrivePoseEstimator poseEstimator, Pose2d targetPose, PathConstraints constraints, Double endVelocity, Boolean reversed) + { + m_poseEstimator = poseEstimator; + m_targetPose = targetPose; + m_constraints = constraints; + m_endVelocity = endVelocity; + m_reversed = reversed; + } + + /** + * This creates and schedules a path on the fly to a given target point. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param endVelocity The velocity it should be at the end of the path. + */ + public OnTheFlyToTarget(DifferentialDrivePoseEstimator poseEstimator, Pose2d targetPose, PathConstraints constraints, Double endVelocity) + { + this(poseEstimator, targetPose, constraints, endVelocity, false); + } + + /** + * This creates and schedules a path on the fly to a given target point. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param reversed If the robot should run the path in reversed (backward) + */ + public OnTheFlyToTarget(DifferentialDrivePoseEstimator poseEstimator, Pose2d targetPose, PathConstraints constraints, Boolean reversed) + { + this(poseEstimator, targetPose, constraints, 0.0, reversed); + } + + /** + * This creates and schedules a path on the fly to a given target point. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + */ + public OnTheFlyToTarget(DifferentialDrivePoseEstimator poseEstimator, Pose2d targetPose, PathConstraints constraints) + { + this(poseEstimator, targetPose, constraints, 0.0, false); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + PathPlannerPath path = PathUtils.OnTheFlyToTarget(currentPose, m_targetPose, m_constraints, m_endVelocity, m_reversed); + + // Create the command version of the path and return it + Command followPath = AutoBuilder.followPath(path); + CommandScheduler.getInstance().schedule(followPath); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OverBumpContinousJ.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OverBumpContinousJ.java new file mode 100644 index 0000000..9100852 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/OverBumpContinousJ.java @@ -0,0 +1,89 @@ +package frc.robot.commands.autoCommands; + +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.InstantCommand; +//import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; +import frc.robot.commands.AimAtHub; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.PathUtils; + +/** An example command that uses an example subsystem. */ +public class OverBumpContinousJ extends Command { + private final DriveSubsystem m_driveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final BooleanSupplier m_isRed; + + /** + * Creates a new OverBumpContinousJ. + * + * @param poseEstimator The pose estimator to get the location of the robot. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + */ + public OverBumpContinousJ(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed) + { + m_driveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_isRed = isRed; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // Deadreckon Over the bump + Command deadreckonFoward1 = new DeadreckonDistance(m_driveSubsystem, 2.5, 2.0); + Command deadreckonFoward2 = new DeadreckonDistance(m_driveSubsystem, 2.5, 2.0); + + // Create paths and scheudle other commands + Command pickupScheudle = new InstantCommand(() -> { + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + + try { + PathPlannerPath ContJ = PathPlannerPath.fromPathFile("ContinuousJ"); + PathPlannerPath editedPath = PathUtils.modifyPath(ContJ, new Pair(0, currentPose)); + //PathPlannerPath editedPath = PathUtils.appendToPath(ContJ, 0, currentPose); + Command pathCommand = AutoBuilder.followPath(editedPath); + + // Create & Scheudle the command group + CommandScheduler.getInstance().schedule( + pathCommand + .andThen(deadreckonFoward2) + .andThen(new AimAtHub(m_driveSubsystem, m_poseEstimator, 2.0, m_isRed)) + .andThen(new DeadreckonDistance(m_driveSubsystem, 0.75, -1.0)) + .andThen(new AimAtHub(m_driveSubsystem, m_poseEstimator, 2.0, m_isRed)) + ); + } catch (Exception e) { + DriverStation.reportError(e.toString(), true); + } + }); + + // Create & Schedule the command group. + //SequentialCommandGroup initialSequential = deadreckonFoward1.andThen(pickupScheudle); + CommandScheduler.getInstance().schedule(pickupScheudle); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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() { + // The command automatically ends + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/TravelToCornerAndShoot.java b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/TravelToCornerAndShoot.java new file mode 100644 index 0000000..f910454 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/autoCommands/TravelToCornerAndShoot.java @@ -0,0 +1,86 @@ +package frc.robot.commands.autoCommands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.BooleanSupplier; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.util.AutoFromList; +import frc.robot.commands.util.AutoFromList.firstPathType; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class TravelToCornerAndShoot extends Command { + private final ArrayList m_preloadPaths = new ArrayList(Arrays.asList( + "ParkAtWall" + )); + + // Pass-ins + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_PoseEstimator; + private final BooleanSupplier m_isRed; + private final PathConstraints m_constraints; + + /** + * Creates a new DSA_BackupTest. + */ + public TravelToCornerAndShoot(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed, PathConstraints constraints) { + m_DriveSubsystem = driveSubsystem; + m_PoseEstimator = poseEstimator; + m_isRed = isRed; + m_constraints = constraints; + + // warm-up for quick load later on + for (Object item : m_preloadPaths) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // Init the new sequence + ArrayList sequence = new ArrayList(Arrays.asList( + "ParkAtWall", + new AimAtHub(m_DriveSubsystem, m_PoseEstimator, 2.0, m_isRed) // Aim at hub + )); + + // Run the command + Command auto = new AutoFromList(sequence, m_constraints, m_DriveSubsystem, m_PoseEstimator, firstPathType.GO_TO_FIRST_PATH, false); + CommandScheduler.getInstance().schedule(auto); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() { + + } + + // 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 true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/util/AutoFromList.java b/2026-Rebuilt/src/main/java/frc/robot/commands/util/AutoFromList.java new file mode 100644 index 0000000..6c9a5f2 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/util/AutoFromList.java @@ -0,0 +1,208 @@ +package frc.robot.commands.util; + +import java.util.List; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; + +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.ConditionalRotation; +import frc.robot.utils.PathUtils; +import frc.robot.utils.PoseDiff; + +// Auto From Path Names And Commands +public class AutoFromList extends Command { + private final List m_sequence; + private final PathConstraints m_constraints; + private final DriveSubsystem m_DriveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final firstPathType m_pathType; + + public enum firstPathType { + NONE, + GO_TO_FIRST_PATH, + REPLACE_FIRST_PATH, + } + + /** + * Creates and runs a new auto from a list of path names and commands. + * Any list with a command can only be ran once due to how the scheudler works. + * By default this command pathfinds to the first path and warm-ups the paths itself. + * + * @param sequence A list of path names (strings) and commands to run in order. + * @param constraints The constraints the pathfinder segment of the auto uses. + * @param driveSubsystem The driveSubsystem, pass-ins for rotation corrections. + * @param poseEstimator The pose estimator, used to get the robots location. + */ + public AutoFromList(List sequence, PathConstraints constraints, DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator) + { + m_sequence = sequence; + m_constraints = constraints; + m_DriveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_pathType = firstPathType.GO_TO_FIRST_PATH; + + warmup(); + } + + /** + * Creates and runs a new auto from a list of path names and commands. + * Any list with a command can only be ran once due to how the scheudler works. + * By default this command warm-ups the paths itself. + * + * @param sequence A list of path names (strings) and commands to run in order. + * @param constraints The constraints the pathfinder segment of the auto uses. + * @param driveSubsystem The driveSubsystem, pass-ins for rotation corrections. + * @param poseEstimator The pose estimator, used to get the robots location. + * @param goToFirstPath Should the command pathfind to the first path? + */ + public AutoFromList(List sequence, PathConstraints constraints, DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, firstPathType pathType) + { + m_sequence = sequence; + m_constraints = constraints; + m_DriveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_pathType = pathType; + + warmup(); + } + + /** + * Creates and runs a new auto from a list of path names and commands. + * Any list with a command can only be ran once due to how the scheudler works. + * + * @param sequence A list of path names (strings) and commands to run in order. + * @param constraints The constraints the pathfinder segment of the auto uses. + * @param driveSubsystem The driveSubsystem, pass-ins for rotation corrections. + * @param poseEstimator The pose estimator, used to get the robots location. + * @param goToFirstPath Should the command go to the first path? + * @param doWarmup Should the command warm-up the paths itself? (If instantiated and ran during run-time, you might want this to be set to false) + */ + public AutoFromList(List sequence, PathConstraints constraints, DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, firstPathType pathType, boolean doWarmup) + { + m_sequence = sequence; + m_constraints = constraints; + m_DriveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_pathType = pathType; + + if (doWarmup == true) { + warmup(); + } + } + + private void warmup() { + for (Object item : m_sequence) { + if (item instanceof String) { + try { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + Command command = AutoBuilder.followPath(path); + new PathPlannerAuto(command); + } catch (Exception e) { + System.out.println(e); + } + } + } + } + + @Override + public void initialize() { + try { + SequentialCommandGroup precommandGroup = new SequentialCommandGroup(); + SequentialCommandGroup commandGroup = new SequentialCommandGroup(); + Boolean pathFinded = m_pathType == firstPathType.NONE; // If we want to pathfind this will be set to false. + + for (Object item : m_sequence) { + if (item instanceof Command) { + if (pathFinded == false) { + precommandGroup.addCommands((Command) item); + System.out.println("Added " + item.getClass().getName() + " to preCommandGroup!"); + } else { + commandGroup.addCommands((Command) item); + System.out.println("Added " + item.getClass().getName() + " to commandGroup!"); + } + } else if (item instanceof String) { + PathPlannerPath path = PathPlannerPath.fromPathFile((String) item); + + if (pathFinded == false) { + Command createEverythingCommand = Commands.runOnce(() -> { + // needed Values + Pose2d pathStartPose = path.getStartingDifferentialPose(); + PoseDiff dPose = new PoseDiff(m_poseEstimator.getEstimatedPosition(), pathStartPose); + Rotation2d targetRotation = new Rotation2d(Math.atan2(dPose.y, dPose.x)); + Command rotateTwordPathStart = ConditionalRotation.New(m_DriveSubsystem, m_poseEstimator, targetRotation, 45, 2.0); + + // path edits + Command pathOnTheFly = Commands.runOnce(() -> { + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + + PathPlannerPath newPath; + if (m_pathType == firstPathType.GO_TO_FIRST_PATH) { + newPath = PathUtils.appendToPath(path, 0, currentPose); + } else if (m_pathType == firstPathType.REPLACE_FIRST_PATH) { + newPath = PathUtils.modifyPath(path, new Pair(0, currentPose)); + } else { + newPath = path; + } + + Command newPathCommand = AutoBuilder.followPath(newPath); + + // schedule the post first-path stuff + System.out.println("Schedueling commandGroup!"); + CommandScheduler.getInstance().schedule( + newPathCommand + .andThen(commandGroup) + ); + }); + + // schedule the first pre-path stuff + System.out.println("Schedueling the path on the fly stuff!"); + CommandScheduler.getInstance().schedule( + rotateTwordPathStart + .andThen(pathOnTheFly) + ); + }); + + pathFinded = true; + precommandGroup.addCommands(createEverythingCommand); + System.out.println("Added createEverything to preCommandGroup!"); + } else { + Command pathCommand = AutoBuilder.followPath(path); + commandGroup.addCommands(pathCommand); + } + } + } + + System.out.println("Schedueling preCommandGroup!"); + CommandScheduler.getInstance().schedule(precommandGroup); + } catch (Exception e) { + // do somthing here + System.out.println("Failed to run " + this.getName() + ": " + e.toString()); + } + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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() { + // The command automatically ends + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/commands/util/CancelDriveCommand.java b/2026-Rebuilt/src/main/java/frc/robot/commands/util/CancelDriveCommand.java new file mode 100644 index 0000000..dcdaefd --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/commands/util/CancelDriveCommand.java @@ -0,0 +1,50 @@ +package frc.robot.commands.util; + +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +/** + * A simple command that merely stops any command currently using the + * robot drivetrain. + **/ +public class CancelDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + + /** + * Creates a new CancelDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public CancelDriveCommand(DriveSubsystem subsystem) + { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + + } + + // 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() { + // The command signals end immediately. + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java b/2026-Rebuilt/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java new file mode 100644 index 0000000..0453ffe --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java @@ -0,0 +1,35 @@ +package frc.robot.factorys; + +import java.util.Optional; +import java.util.function.BooleanSupplier; + +import com.ctre.phoenix6.hardware.TalonFX; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.Pigeon; + +public class DriveSubsystemFactory { + public DriveSubsystemFactory() { + } + + public Optional construct(DifferentialDrivePoseEstimator poseEstimator, + DifferentialDriveKinematics kinematics, + Pigeon pigeon, Optional rightLead, Optional leftLead, Optional rightFollower, + Optional leftFollower, BooleanSupplier isRed) { + + if (rightLead.isEmpty() || leftLead.isEmpty()) { + return Optional.empty(); + } + + DriveSubsystem driveSubsystem = new DriveSubsystem(poseEstimator, kinematics, pigeon, rightLead.get(), + leftLead.get(), isRed); + + if (rightFollower.isPresent() && leftFollower.isPresent()) { + driveSubsystem.setFollowers(rightFollower.get(), leftFollower.get()); + } + + return Optional.of(driveSubsystem); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/factorys/TalonFXFactory.java b/2026-Rebuilt/src/main/java/frc/robot/factorys/TalonFXFactory.java new file mode 100644 index 0000000..e50c14e --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/factorys/TalonFXFactory.java @@ -0,0 +1,19 @@ +package frc.robot.factorys; + +import java.util.Optional; + +import com.ctre.phoenix6.hardware.TalonFX; + +public class TalonFXFactory { + public TalonFXFactory() {} + + public Optional construct(int CANID) { + TalonFX talon = new TalonFX(CANID); + + if (talon.isConnected()) { + return Optional.of(talon); + } else { + return Optional.empty(); + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/ClimbL1Subsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/ClimbL1Subsystem.java new file mode 100644 index 0000000..b19916f --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/ClimbL1Subsystem.java @@ -0,0 +1,95 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.Solenoid; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.robot.Constants.ClimbL1Constants; +import frc.robot.Constants.PneumaticsConstants; + +public class ClimbL1Subsystem extends SubsystemBase { + private Solenoid m_LeftSolenoid; + private Solenoid m_RightSolenoid; + private boolean m_bRaised = false; + + /** Creates a new ClimbL1Subsystem. + * @throws Exception */ + public ClimbL1Subsystem(Boolean bHasPneumatics) throws Exception { + + // Create SmartDashboard items regardless of whether or not we instantiate + // the subsystem. + SmartDashboard.putBoolean("ClimbL1Raised", m_bRaised); + SmartDashboard.putBoolean("ClimbL1 TestRaise", false); + + // Create member objects in the constructor in the hope that they throw exceptions + // if underlying hardware is not present and, hence, allow our Optional system to + // work. + if (bHasPneumatics) { + m_LeftSolenoid = new Solenoid(PneumaticsConstants.kPneumaticsHubCANID, + PneumaticsConstants.kHubType, + ClimbL1Constants.kLeftSolenoidChannel); + m_RightSolenoid = new Solenoid(PneumaticsConstants.kPneumaticsHubCANID, + PneumaticsConstants.kHubType, + ClimbL1Constants.kLeftSolenoidChannel); + } + else { + throw new Exception("Climb subsystem requires pneumatics which are not present!"); + } + } + + // Raise the climb arms. + public void raiseArms() + { + m_LeftSolenoid.set(ClimbL1Constants.kClimbL1Raise); + m_RightSolenoid.set(ClimbL1Constants.kClimbL1Raise); + m_bRaised = true; + SmartDashboard.putBoolean("ClimbL1Raised", m_bRaised); + } + + // Lower the climb arms. + public void lowerArms() + { + m_LeftSolenoid.set(ClimbL1Constants.kClimbL1Lower); + m_RightSolenoid.set(ClimbL1Constants.kClimbL1Lower); + m_bRaised = false; + SmartDashboard.putBoolean("ClimbL1Raised", m_bRaised); + } + + // Determine whether the arms are currently in the raised or lowered position. + // NB: The robot has no way to know the current position of the arms so this merely + // returns the last commanded state! Commands using this method may need to start a + // timer when this state changes to allow for the time taken to move the pneumatics. + public boolean areArmsRaised() + { + return m_bRaised; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + // raise and lowers climb + public void testPeriodic() { + Boolean RaiseClimb = SmartDashboard.getBoolean("ClimbL1 TestRaise", false); + + if(RaiseClimb) { + if(!m_bRaised) { + this.raiseArms(); + } + } + else { + if(m_bRaised) { + this.lowerArms(); + } + } + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/DriveSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/DriveSubsystem.java new file mode 100644 index 0000000..e2a1a39 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/DriveSubsystem.java @@ -0,0 +1,331 @@ +// 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. + +// https://github.com/CrossTheRoadElec/Phoenix6-Examples/blob/main/java/VelocityClosedLoop/src/main/java/frc/robot/Robot.java +// This is a link to do speed control on the kraken motors. We need something like this. +package frc.robot.subsystems; +import static edu.wpi.first.units.Units.Volts; + +import java.util.function.BooleanSupplier; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.controls.MotionMagicVelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.MotorAlignmentValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.controllers.PPLTVController; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants.AutoConstants; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.commands.DriveCommands.ArcadeDriveCommand; +import frc.robot.commands.DriveCommands.ArcadeDriveCommandNoPID; +import frc.robot.commands.DriveCommands.ArcadeFromDashboard; +import frc.robot.commands.DriveCommands.DifferentialDriveCommand; +import frc.robot.commands.DriveCommands.NoDriveCommand; +import frc.robot.utils.Pigeon; + +public class DriveSubsystem extends SubsystemBase { + private TalonFX m_rightMotor; + private TalonFX m_optionalRightMotor; + + private TalonFX m_leftMotor; + private TalonFX m_optionalLeftMotor; + + /* Start at velocity 0, use slot 0 */ + private final MotionMagicVelocityVoltage m_leftVelocityVoltage = new MotionMagicVelocityVoltage(0);//.withSlot(0); + private final MotionMagicVelocityVoltage m_rightVelocityVoltage = new MotionMagicVelocityVoltage(0);//.withSlot(0); + + private final DifferentialDriveKinematics m_kinematics; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final Pigeon m_pigeon; + + private final SendableChooser> m_driveChooser = new SendableChooser<>(); + + /** Creates a new DriveSubsystem. */ + public DriveSubsystem(DifferentialDrivePoseEstimator poseEstimator, DifferentialDriveKinematics kinematics, Pigeon pigeon, TalonFX rightMotor, TalonFX leftMotor, BooleanSupplier isRed) { + m_poseEstimator = poseEstimator; + m_kinematics = kinematics; + m_pigeon = pigeon; + m_rightMotor = rightMotor; + m_leftMotor = leftMotor; + + // Right Motor + setConfig(m_rightMotor, InvertedValue.CounterClockwise_Positive); + m_rightMotor.set(0.0); + SendableRegistry.setName(m_rightMotor, "DriveSubsystem", "rightMotor"); + + // Left Motor + setConfig(m_leftMotor, InvertedValue.Clockwise_Positive); + m_leftMotor.set(0.0); + SendableRegistry.setName(m_leftMotor, "DriveSubsystem", "leftMotor"); + + // Zeroing the encoders + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + + // Init Autobuilder + AutoBuilder.configure( + this::getPose, + this::resetPose, + this::getRobotRelativeSpeeds, + (speeds, feedforwards) -> driveRobotRelative(speeds), + new PPLTVController(0.02), + AutoConstants.kRobotConfig, + isRed, + this + ); + + // Gyro setup + //m_pigeon.reset(); + + // Drive Chooser Init + m_driveChooser.setDefaultOption("PID Arcade", ArcadeDriveCommand.class); + m_driveChooser.addOption("PID Differential", DifferentialDriveCommand.class); + m_driveChooser.addOption("NO-PID Arcade", ArcadeDriveCommandNoPID.class); + m_driveChooser.addOption("Dashboard PID Arcade", ArcadeFromDashboard.class); + m_driveChooser.addOption("None", NoDriveCommand.class); + SmartDashboard.putData("Drive Commands", m_driveChooser); + + // Init Smartdashboard + SmartDashboard.putNumber("DB Left Set (MPS)", 0); + SmartDashboard.putNumber("DB Right Set (MPS)", 0); + SmartDashboard.putNumber("DB Arcade Set (MPS)", 0); + + SmartDashboard.putNumber("Left Target (MPS)", 0); + SmartDashboard.putNumber("Right Target (MPS)", 0); + SmartDashboard.putNumber("Left Speed (MPS)", getMotorSpeedMPS(true)); + SmartDashboard.putNumber("Right Speed (MPS)", getMotorSpeedMPS(false)); + SmartDashboard.putNumber("Left Speed (RPS)", getMotorSpeedRPS(true)); + SmartDashboard.putNumber("Right Speed (RPS)", getMotorSpeedRPS(false)); + + SmartDashboard.putNumber("Left Set (-1-->1)", 0); + SmartDashboard.putNumber("Right Set (-1-->1)", 0); + SmartDashboard.putNumber("Left Get (-1-->1)", 0); + SmartDashboard.putNumber("Right Get (-1-->1)", 0); + + SmartDashboard.putNumber("Gyro Degrees", m_pigeon.getYaw()); + } + + private void setConfig(TalonFX motor, InvertedValue Inverted) { + TalonFXConfiguration motorConfig = new TalonFXConfiguration(); + motorConfig.Slot0.kS = DrivetrainConstants.kS; + motorConfig.Slot0.kV = DrivetrainConstants.kV; + motorConfig.Slot0.kA = DrivetrainConstants.kA_linear; + motorConfig.Slot0.kP = DrivetrainConstants.kP; + motorConfig.Slot0.kI = DrivetrainConstants.kI; // No output for integrated error + motorConfig.Slot0.kD = DrivetrainConstants.kD; // A velocity of 1 rps results in 0.1 V output + + motorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + motorConfig.MotorOutput.Inverted = Inverted; + + motorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.kPeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.kPeakVoltage)); + + var motionMagicConfigs = motorConfig.MotionMagic; + motionMagicConfigs.MotionMagicAcceleration = DrivetrainConstants.kMotionMagicAcceleration; + motionMagicConfigs.MotionMagicJerk = DrivetrainConstants.kMotionMagicJerk; + + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = motor.getConfigurator().apply(motorConfig); + System.out.println("config attempt #" + (i+1) + ": " + status.toString()); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs, error code: " + status.toString()); + } + } + + public void setFollowers(TalonFX optionalRight, TalonFX optionalLeft) { + // Right Motor + m_optionalRightMotor = optionalRight; + + setConfig(m_optionalRightMotor, InvertedValue.CounterClockwise_Positive); + m_optionalRightMotor.setControl( + new Follower(m_rightMotor.getDeviceID(), MotorAlignmentValue.Aligned) + ); + + // Left Motor + m_optionalLeftMotor = optionalLeft; + + setConfig(m_optionalLeftMotor, InvertedValue.Clockwise_Positive); + m_optionalLeftMotor.setControl( + new Follower(m_leftMotor.getDeviceID(), MotorAlignmentValue.Aligned) + ); + } + + // This takes the current class selected in the drive chooser and attempts to initilize a command object from it & run it. + // The commands must have a constructor with a DriveSubsystem and CommandXboxController as args. + public void initDefaultCommand(CommandXboxController Controller) + { + Class driveClass = m_driveChooser.getSelected(); + + try { + Object driveCommand = driveClass.getDeclaredConstructor(DriveSubsystem.class, CommandXboxController.class).newInstance(this, Controller); + setDefaultCommand((Command) driveCommand); + + System.out.println(String.format("Loaded drive command \"%s\"!", driveClass.getName())); + } catch (Exception e) { + DriverStation.reportWarning( + String.format( + "Failed to load drive command \"%s\"!", driveClass.getName()), + false + ); + + e.printStackTrace(); + } + + // setDefaultCommand(new ArcadeDriveCommand(this, Controller)); + } + + // This converts rotations of the motor shaft to meters travled. + public double ConvertRotationsToMeters(double rotations) { + return rotations * DrivetrainConstants.kDrivetrainGearRatio * DrivetrainConstants.kWheelCircumference; + } + + // This converts meters travled to rotations of the motor shaft. + // This is the inverse function of the one above, calculated it myself. - Owen + public double ConvertMetersToRotations(double meters) { + return (meters / DrivetrainConstants.kDrivetrainGearRatio) / DrivetrainConstants.kWheelCircumference; + } + + // This sets the differential speeds based on a desired MPS speed. + public void setDifferentialSpeeds(double leftSpeedMPS, double rightSpeedMPS) + { + //System.out.println("🙀 " + String.valueOf(leftSpeedMPS) + ", " + String.valueOf(rightSpeedMPS)); + + SmartDashboard.putNumber("Left Target (MPS)", leftSpeedMPS); + SmartDashboard.putNumber("Right Target (MPS)", rightSpeedMPS); + + double leftSpeedRPS = ConvertMetersToRotations(leftSpeedMPS); + double rightSpeedRPS = ConvertMetersToRotations(rightSpeedMPS); + + SmartDashboard.putNumber("Left Target (RPS)", leftSpeedRPS); + SmartDashboard.putNumber("Right Target (RPS)", rightSpeedRPS); + + m_leftMotor.setControl(m_leftVelocityVoltage.withVelocity(leftSpeedRPS)); + m_rightMotor.setControl(m_rightVelocityVoltage.withVelocity(rightSpeedRPS)); + } + + // pass in -1 to 1 for the motor speed with no closed loop control. + public void setDifferentialSpeedNoPid(double left, double right) { + SmartDashboard.putNumber("Left Set (-1-->1)", left); + SmartDashboard.putNumber("Right Set (-1-->1)", right); + + m_leftMotor.set(left); + m_rightMotor.set(right); + } + + // Wrapping robot position inside of getposition + public Pose2d getPose(){ + return m_poseEstimator.getEstimatedPosition(); + } + + // Resets the drivetrains pose estimator to zero. + public void resetPose(Pose2d newPose){ + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + + m_poseEstimator.resetPosition( + m_pigeon.getRotation2d(), + 0, 0, + newPose + ); + } + + public double getMotorSpeedRPS(boolean bLeft) + { + double RPS; + if (bLeft) + { + RPS = m_leftMotor.getVelocity().getValueAsDouble(); + } + else + { + RPS = m_rightMotor.getVelocity().getValueAsDouble(); + } + return RPS; + } + + public double getMotorSpeedMPS(boolean bLeft) + { + double RPS = getMotorSpeedRPS(bLeft); + double MPS = ConvertRotationsToMeters(RPS); + return MPS; + } + + // Gets the distance traveled by the motor in meters + public double getMotorPositionMeters(boolean bLeft) { + double posRotations; + if (bLeft == true) { + posRotations = m_leftMotor.getPosition().getValueAsDouble(); + } else { + posRotations = m_rightMotor.getPosition().getValueAsDouble(); + } + + double posMeters = ConvertRotationsToMeters(posRotations); + return posMeters; + } + + // Returns a robot relative ChassisSpeeds object based on the average linear velocity + // in meters per second and average angular velocity in radians per second. + // Currently we are assuming that there is no scale for the motors, we cannot find anywhere to set the scale. + public ChassisSpeeds getRobotRelativeSpeeds() + { + // Linear velocity in meters per second + double leftMPS = getMotorSpeedMPS(true); + double rightMPS = getMotorSpeedMPS(false); + + // Make wheelSpeeds object from MPS & converts it to chasis speeds + DifferentialDriveWheelSpeeds wheelSpeeds = new DifferentialDriveWheelSpeeds(leftMPS, rightMPS); + return m_kinematics.toChassisSpeeds(wheelSpeeds); + } + + // Gives the drivetrain a new drive command based on a robot relative + // chassis speed object. + public void driveRobotRelative(ChassisSpeeds relativeChassisSpeed){ + DifferentialDriveWheelSpeeds wheelSpeeds = m_kinematics.toWheelSpeeds(relativeChassisSpeed); + + setDifferentialSpeeds(wheelSpeeds.leftMetersPerSecond, wheelSpeeds.rightMetersPerSecond); + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + double lPositionMeters = getMotorPositionMeters(true); + double rPositionMeters = getMotorPositionMeters(false); + Rotation2d yaw = m_pigeon.getRotation2d(); + + m_poseEstimator.update( + yaw, + lPositionMeters, + rPositionMeters + ); + + SmartDashboard.putNumber("Left Get (-1-->1)", m_leftMotor.get()); + SmartDashboard.putNumber("Right Get (-1-->1)", m_rightMotor.get()); + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/IndexerBulkSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/IndexerBulkSubsystem.java new file mode 100644 index 0000000..6b5c93c --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/IndexerBulkSubsystem.java @@ -0,0 +1,182 @@ +// +// Subsystem offering low level control of the actuators and sensors +// in the indexer and bulk transport mechanisms. +// + +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.robot.Constants.IndexerBulkConstants; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXSConfiguration; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.hardware.TalonFXS; +import com.ctre.phoenix6.signals.MotorArrangementValue; + + +public class IndexerBulkSubsystem extends SubsystemBase { + + private TalonFXS m_motorBulk; + private TalonFX m_motorIndexer; + private Boolean m_bulkStarted = false; + private Boolean m_indexerStarted = false; + private double m_bulkSpeed = IndexerBulkConstants.kBulkMoveMotorSpeed; + private double m_indexerSpeed = IndexerBulkConstants.kIndexerMotorSpeed; + + /** Creates a new IndexerBulkSubsystem. */ + public IndexerBulkSubsystem() throws Exception { + + // Create SmartDashboard items first so that these are populated + // regardless of whether or not the subsystem is present. + SmartDashboard.putBoolean("IndexerBulk BulkStarted", m_bulkStarted); + SmartDashboard.putBoolean("IndexerBulk IndexerStarted", m_indexerStarted); + SmartDashboard.putNumber("IndexerBulk BulkSpeed", m_bulkSpeed); + SmartDashboard.putNumber("IndexerBulk IndexerSpeed", m_indexerSpeed); + + // Test mode controls + SmartDashboard.putBoolean("IndexerBulk TestBulk", false); + SmartDashboard.putBoolean("IndexerBulk TestIndexer", false); + SmartDashboard.putNumber("IndexerBulk TestBulkSpeed", m_bulkSpeed); + SmartDashboard.putNumber("IndexerBulk TestIndexerSpeed", m_indexerSpeed); + + m_motorBulk = new TalonFXS(IndexerBulkConstants.kBulkMoveMotorCANID); + if (!m_motorBulk.isConnected()) + { + throw new Exception("Bult transport motor is not present."); + } + + m_motorIndexer = new TalonFX(IndexerBulkConstants.kIndexerMotorCANID); + if (!m_motorIndexer.isConnected()) + { + throw new Exception("Indexer motor is not present."); + } + + m_motorBulk.set(0.0); + m_motorIndexer.set(0.0); + + TalonFXSConfiguration motorConfig = new TalonFXSConfiguration(); + motorConfig.Commutation.MotorArrangement = MotorArrangementValue.Minion_JST; + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = m_motorBulk.getConfigurator().apply(motorConfig); + System.out.println("bulk transfer config attempt #" + (i+1) + ": " + status.toString()); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs for bulk tranfer motor, error code: " + status.toString()); + } + } + +// Runs the belts that transfer the fuel from bulk storage to the indexer. + public void startBulkTransfer() { + m_motorBulk.set(m_bulkSpeed); + m_bulkStarted = true; + SmartDashboard.putBoolean("IndexerBulk BulkStarted", m_bulkStarted); + } + + public void reverseBulkTransfer() { + m_motorBulk.set(-m_bulkSpeed/2); + m_bulkStarted = true; + SmartDashboard.putBoolean("IndexerBulk BulkStarted", m_bulkStarted); + } + +// Stops the belts in bulk storage. + public void stopBulkTransfer() { + m_motorBulk.set(0.0); + m_bulkStarted = false; + SmartDashboard.putBoolean("IndexerBulk BulkStarted", m_bulkStarted); + } + + public void setBulkTransferSpeed(double speed) { + m_bulkSpeed = speed; + if(m_bulkStarted) + { + startBulkTransfer(); + } + SmartDashboard.putNumber("IndexerBulk BulkSpeed", m_bulkSpeed); + } + + // Runs the indexer. + public void startIndexer() { + // Indexer runs in the negative direction when operating normally so we negate the commanded speed. + m_motorIndexer.set(-m_indexerSpeed); + m_indexerStarted = true; + SmartDashboard.putBoolean("IndexerBulk IndexerStarted", m_indexerStarted); + } + + public void reverseIndexer() { + // Indexer runs in the negative direction when operating normally so we dont the commanded speed. + m_motorIndexer.set(m_indexerSpeed/2); + m_indexerStarted = true; + SmartDashboard.putBoolean("IndexerBulk IndexerStarted", m_indexerStarted); + } + +// Stops the indexer. + public void stopIndexer() { + m_motorIndexer.set(0.0); + m_indexerStarted = false; + SmartDashboard.putBoolean("IndexerBulk IndexerStarted", m_indexerStarted); + } + + public void setIndexerSpeed(double speed) { + m_indexerSpeed = speed; + if(m_indexerStarted) + { + startIndexer(); + } + + SmartDashboard.putNumber("IndexerBulk IndexerSpeed", m_indexerSpeed); + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } + + public void testPeriodic() { + // Set the motor states based on the SmartDashboard test controls. + Boolean BulkOn = SmartDashboard.getBoolean("IndexerBulk TestBulk", false); + Boolean IndexerOn = SmartDashboard.getBoolean("IndexerBulk TestIndexer", false); + double BulkSpeed = SmartDashboard.getNumber("IndexerBulk TestBulkSpeed", m_bulkSpeed); + double IndexerSpeed = SmartDashboard.getNumber("IndexerBulk TestIndexerSpeed", m_indexerSpeed); + + // Set new target speeds for each motor. + setBulkTransferSpeed(BulkSpeed); + setIndexerSpeed(IndexerSpeed); + + // Turn the bulk transfer on or off depending upon test control, only changing the state if the control actually changed. + if(BulkOn) { + if(!m_bulkStarted) { + this.startBulkTransfer(); + } + } + else { + if(m_bulkStarted) { + this.stopBulkTransfer(); + } + } + + // Turn the indexer on or off depending upon test control, only changing the state if the control actually changed. + if(IndexerOn) { + if(!m_indexerStarted) { + this.startIndexer(); + } + } + else { + if(m_indexerStarted) { + this.stopIndexer(); + } + } + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/IntakeSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/IntakeSubsystem.java new file mode 100644 index 0000000..51337ee --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/IntakeSubsystem.java @@ -0,0 +1,140 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.DoubleSolenoid; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.robot.Constants.IntakeConstants; +import frc.robot.Constants.PneumaticsConstants; + +public class IntakeSubsystem extends SubsystemBase { + private TalonFX m_motorIntake; + private DoubleSolenoid m_solenoid; + private boolean m_motorRunning = false; + private boolean m_Extended = false; + private double m_MotorSpeed = IntakeConstants.kIntakeMotorSpeed; + + /** Creates a new IntakeSubsystem. */ + public IntakeSubsystem(Boolean bHasPneumatics) throws Exception { + + // Create SmartDashboard items first so that these exist even + // if we fail to create the subsystem. + SmartDashboard.putBoolean("Intake Extended", m_Extended); + SmartDashboard.putBoolean("Intake Running", m_motorRunning); + + // Test mode controls + SmartDashboard.putBoolean("Intake TestExtend", false); + SmartDashboard.putBoolean("Intake TestRun", false); + SmartDashboard.putNumber("Intake TestSpeed", m_MotorSpeed); + + m_motorIntake = new TalonFX(IntakeConstants.kIntakeMotorCANID); + if(!m_motorIntake.isConnected()) + { + throw new Exception("Intake motor is not connected!"); + } + m_motorIntake.set(0.0); + + if(bHasPneumatics) { + m_solenoid = new DoubleSolenoid(PneumaticsConstants.kPneumaticsHubCANID, + PneumaticsConstants.kHubType, + IntakeConstants.kIntakeSolenoidForward, + IntakeConstants.kIntakeSolenoidReverse); + } else { + throw new Exception("Intake subsystem requires pneumatics which are not present!"); + } + } + + // Spins the shaft on the intake that will move fuel into the robot. + public void start() { + m_motorIntake.set(-m_MotorSpeed); + m_motorRunning = true; + SmartDashboard.putBoolean("Intake Running", m_motorRunning); + } + + // Stops spinning the intake shaft. + public void stop() { + m_motorIntake.set(0.0); + m_motorRunning = false; + SmartDashboard.putBoolean("Intake Running", m_motorRunning); + } + public void extend() { + m_solenoid.set(IntakeConstants.kIntakeExtend); + m_Extended = true; + SmartDashboard.putBoolean("Intake Extended", m_Extended); + } + + // Retracts the intake mechanism over the bumpers and inside of the robot. + public void retract() { + m_solenoid.set(IntakeConstants.kIntakeRetract); + m_Extended = false; + SmartDashboard.putBoolean("Intake Extended", m_Extended); + } + + // Retracts the intake mechanism over the bumpers and inside of the robot. + public void pneumatics_off() { + m_solenoid.set(DoubleSolenoid.Value.kOff); + } + + // Returns true if the intake shaft is spinning. + public boolean isIntakeStarted() { + return m_motorRunning; + } + + // Returns true if the intake is extended outside of the frame perimiter. + public boolean isIntakeExtended() { + return m_Extended; + } + + public void setIntakeSpeed(double speed) { + m_MotorSpeed = speed; + if(m_motorRunning) + { + start(); + } + } + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + public void testPeriodic() { + // Set the motor states based on the SmartDashboard test controls. + Boolean Extend = SmartDashboard.getBoolean("Intake TestExtend", false); + Boolean Run = SmartDashboard.getBoolean("Intake TestRun", false); + double Speed = SmartDashboard.getNumber("Intake TestSpeed", m_MotorSpeed); + + setIntakeSpeed(Speed); + + // Turn the intake on or off depending upon test control, only changing the state if the control actually changed. + if(Run) { + if(!m_motorRunning) { + this.start(); + } + } + else { + if(m_motorRunning) { + this.stop(); + } + } + if(Extend) { + if(!m_Extended) { + this.extend(); + } + } + else { + if(m_Extended) { + this.retract(); + } + } + } + + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/LauncherSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LauncherSubsystem.java new file mode 100644 index 0000000..d801903 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LauncherSubsystem.java @@ -0,0 +1,231 @@ +// +// Subsystem offering low level control of the launcher flywheel and +// fuel lift mechanisms. +// + +// 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 frc.robot.subsystems; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.MotionMagicVelocityVoltage; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.*; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.DigitalInput; +import frc.robot.Constants.LauncherConstants; + +public class LauncherSubsystem extends SubsystemBase { + private double m_targetSpeedrpm = 0.0; + private boolean m_HasFuel = false; + private int m_tickCount = 0; + private boolean m_configValid = false; + + private TalonFX m_motorLeader; + private TalonFX m_motorFollower; + private TalonFXConfiguration m_MotorConfigs = new TalonFXConfiguration(); + + private double m_P = LauncherConstants.kP; + private double m_I = LauncherConstants.kI; + private double m_D = LauncherConstants.kD; + private double m_MMAccel = LauncherConstants.kMotionMagicAcceleration; + private double m_MMJerk = LauncherConstants.kMotionMagicJerk; + + private final DigitalInput m_Sensor = new DigitalInput(LauncherConstants.kUpperFuelSensorChannel); + + /* Be able to switch which control request to use based on a button press */ + /* Start at velocity 0, use slot 0 */ + private final MotionMagicVelocityVoltage m_velocityVoltage = new MotionMagicVelocityVoltage(0).withSlot(0); + + /** Creates a new LauncherSubsystem. + * @throws Exception */ + public LauncherSubsystem() throws Exception { + + // Create SmartDashboard items first so that these are populated + // regardless of whether or not the subsystem is present. + SmartDashboard.putNumber("Launcher RPM", 0.0 ); + SmartDashboard.putNumber("Launcher RPS", 0.0); + SmartDashboard.putBoolean("Launcher Has Fuel", m_HasFuel); + + // Test mode controls + SmartDashboard.putNumber("Launcher TestRPM", 0.0 ); + SmartDashboard.putNumber("Launcher TestP", m_P ); + SmartDashboard.putNumber("Launcher TestI", m_I ); + SmartDashboard.putNumber("Launcher TestD", m_D ); + SmartDashboard.putNumber("Launcher TestMMAccel", m_MMAccel ); + SmartDashboard.putNumber("Launcher TestMMJerk", m_MMJerk ); + + m_motorLeader = new TalonFX(LauncherConstants.kLauncherFlywheelMotor1CANID); + m_motorFollower = new TalonFX(LauncherConstants.kLauncherFlywheelMotor2CANID); + + // Check that the launcher motors exist and throw an exception if they don't. + if(!m_motorLeader.isConnected() || !m_motorFollower.isConnected()) + { + throw new Exception("At least one launcher motor is not present!"); + } + + setMotorPIDConfigs(m_MotorConfigs); + + // Set coast mode so that the flywheel doesn't slam to a halt when the + // motors stop. + m_MotorConfigs.MotorOutput.NeutralMode = NeutralModeValue.Coast; + + // Set the basic rotation direction for both motors. Note that this setting + // will be ignored by the follower since we explicitly tell it whether to + // run in the same direction as or the opposite direction from the leader + // when we set up the follow relationship below. + m_MotorConfigs.MotorOutput.Inverted = LauncherConstants.kLauncherMotorForwardIsCCW ? + InvertedValue.CounterClockwise_Positive : + InvertedValue.Clockwise_Positive; + applyMotorConfigs(m_MotorConfigs); + + // Set up the follower. Note that setting MotorAlignmentValue tells the system to ignore the + // follower's MotorOutput.Inverted setting and either run the same direction as, or the opposite + // direction from, the leader. + m_motorFollower.setControl(new Follower(m_motorLeader.getDeviceID(), + LauncherConstants.kMotorsDriveInOppositeDirections ? + MotorAlignmentValue.Opposed : MotorAlignmentValue.Aligned)); + + m_motorLeader.set(0.0); + + m_configValid = true; + } + + public void applyMotorConfigs(TalonFXConfiguration configs) + { + /* Retry config apply up to 5 times, report if failure */ + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = m_motorLeader.getConfigurator().apply(configs); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs 1, error code: " + status.toString()); + } + for (int i = 0; i < 5; ++i) { + status = m_motorFollower.getConfigurator().apply(configs); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs 2, error code: " + status.toString()); + } + } + + public void setMotorPIDConfigs(TalonFXConfiguration configs) + { + /* Voltage-based velocity requires a velocity feed forward to account for the back-emf of the motor */ + configs.Slot0.kS = LauncherConstants.kS; // To account for friction, add 0.1 V of static feedforward + configs.Slot0.kV = LauncherConstants.kV; // Kraken X60 is a 500 kV motor, 500 rpm per V = 8.333 rps per V, 1/8.33 = 0.12 volts / rotation per second + configs.Slot0.kP = m_P; // An error of 1 rotation per second results in 0.11 V output + configs.Slot0.kI = m_I; // No output for integrated error + configs.Slot0.kD = m_D; // No output for error derivative + // Peak output of 8 volts + configs.Voltage.withPeakForwardVoltage(Volts.of(8)) + .withPeakReverseVoltage(Volts.of(-8)); + + var motionMagicConfigs = configs.MotionMagic; + motionMagicConfigs.MotionMagicAcceleration = m_MMAccel; + motionMagicConfigs.MotionMagicJerk = m_MMJerk; + } + + // + // Set the speed of the launcher flywheel in revolutions per minute. + // + public void setTargetSpeedrpm(double RPM) + { + m_targetSpeedrpm = RPM; + + // changed from multiplying to dividing + + double desiredRotationsPerSecond = RPM / 60.0; + + SmartDashboard.putNumber("Launcher Set RPM", RPM); + SmartDashboard.putNumber("Launcher Set RPS", desiredRotationsPerSecond); + + /* Use velocity voltage */ + m_motorLeader.setControl(m_velocityVoltage.withVelocity(desiredRotationsPerSecond)); + } + + // + // Get the current launcher flywheel speed in revolutions per minute. + // + public double getCurrentSpeedrpm() + { + // Remember to convert motor's revs per second velocity to revs per minute! + return m_motorLeader.getVelocity().getValueAsDouble() * 60.0; + } + + // + // Get the current target speed for the launcher flywheel in revolutions + // per minute. + // + public double getTargetSpeedrpm() + { + return m_targetSpeedrpm; + } + + // + // Determine whether or not a fuel is in position beneath the launcher + // entrance. + // + public boolean isFuelAtLauncher() + { + boolean sensorRead = m_Sensor.get(); + m_HasFuel = (sensorRead == LauncherConstants.kUpperFuelSensorIsEmpty) ? false : true; + return m_HasFuel; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + m_tickCount++; + + + if(m_configValid && ((m_tickCount % LauncherConstants.kTicksPerUpdate) == 0)) + { + double speedRPS = m_motorLeader.getVelocity().getValueAsDouble(); + SmartDashboard.putNumber("Launcher RPM", speedRPS * 60.0); + SmartDashboard.putNumber("Launcher RPS", speedRPS); + SmartDashboard.putBoolean("Launcher Has Fuel", isFuelAtLauncher()); + } + } + + public void testPeriodic() { + + double NewP = SmartDashboard.getNumber("Launcher TestP", m_P ); + double NewI = SmartDashboard.getNumber("Launcher TestI", m_I ); + double NewD = SmartDashboard.getNumber("Launcher TestD", m_D ); + double NewMMAccel = SmartDashboard.getNumber("Launcher TestMMAccel", m_MMAccel ); + double NewMMJerk = SmartDashboard.getNumber("Launcher TestMMJerk", m_MMJerk ); + + // Did any of the PID tuning parameters change? + if ((NewP != m_P) || (NewI != m_I) || (NewD != m_D) || (NewMMAccel != m_MMAccel) || (NewMMJerk != m_MMJerk)) + { + // Yes - something changed. Update the motor configuration to reflect the new tuning parameters. + m_P = NewP; + m_I = NewI; + m_D = NewD; + m_MMAccel = NewMMAccel; + m_MMJerk = NewMMJerk; + + setMotorPIDConfigs(m_MotorConfigs); + applyMotorConfigs(m_MotorConfigs); + } + + double launcherrpm = SmartDashboard.getNumber("Launcher TestRPM", 0.0 ); + this.setTargetSpeedrpm(launcherrpm); + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/LiftSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LiftSubsystem.java new file mode 100644 index 0000000..9c30d7d --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LiftSubsystem.java @@ -0,0 +1,108 @@ +// +// Subsystem offering low level control of the launcher flywheel and +// fuel lift mechanisms. +// + +// 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 frc.robot.subsystems; + +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.Constants.LauncherConstants; + +public class LiftSubsystem extends SubsystemBase { + private boolean m_liftRunning = false; + + private TalonFX m_motorLift; + private double m_liftSpeed = LauncherConstants.kLiftMotorSpeed; + + /** Creates a new LiftSubsystem. + * @throws Exception */ + public LiftSubsystem() throws Exception { + + // Test mode controls + SmartDashboard.putNumber("Lift TestLiftSpeed", LauncherConstants.kLiftMotorSpeed); + SmartDashboard.putBoolean("Lift TestLiftRun", false); + + + m_motorLift = new TalonFX(LauncherConstants.kLauncherLiftMotorCANID); + + // Check that the launcher motors exist and throw an exception if they don't. + if(!m_motorLift.isConnected()) + { + throw new Exception("Lift motor is not present!"); + } + + m_motorLift.set(0.0); + } + + // + // Start the lift mechanism motor. + // + public void startLift() + { + m_motorLift.set(m_liftSpeed); + m_liftRunning = true; + } + + // + // Stop the lift mechanism motor. + // + public void stopLift() + { + m_motorLift.set(0.0); + m_liftRunning = false; + } + + public void setLiftSpeed(double speed) + { + m_liftSpeed = speed; + + if(m_liftRunning) + { + startLift(); + } + } + + // + // Determine whether or not the lift motor is running. + public boolean isLiftMotorStarted() + { + return m_liftRunning; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + public void testPeriodic() { + + double liftspeed = SmartDashboard.getNumber("Lift TestLiftSpeed", 0.0 ); + Boolean liftRun = SmartDashboard.getBoolean("Lift TestLiftRun", false); + + this.setLiftSpeed(liftspeed); + + // Turn the intake on or off depending upon test control, only changing the state if the control actually changed. + if(liftRun) { + if(!m_liftRunning) { + this.startLift(); + } + } + else { + if(m_liftRunning) { + this.stopLift(); + } + } + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/subsystems/LimelightSubsystem.java b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LimelightSubsystem.java new file mode 100644 index 0000000..c0bd0b6 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/subsystems/LimelightSubsystem.java @@ -0,0 +1,126 @@ +// 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 frc.robot.subsystems; + +import java.util.Arrays; + +import org.ejml.simple.SimpleMatrix; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.LimelightHelpers; +import frc.robot.Constants.FieldConstants; +import frc.robot.LimelightHelpers.PoseEstimate; +import frc.robot.utils.LimitedQueue; + +public class LimelightSubsystem extends SubsystemBase { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final LimitedQueue m_YawQue = new LimitedQueue(5); + private boolean m_allowJumps = true; + + /** Creates a new PositionSubsystem. */ + public LimelightSubsystem(DifferentialDrivePoseEstimator poseEstimator) { + m_poseEstimator = poseEstimator; + } + + public Double getRobotYaw() { + if (m_YawQue.isEmpty()) { + return null; + } + + Double[] doubleObjects = m_YawQue.toArray(new Double[0]); + int queSize = doubleObjects.length; + + double[] primitiveDoubles = new double[queSize]; + for (int i = 0; i < queSize; i++) { + primitiveDoubles[i] = doubleObjects[i].doubleValue(); + } + + SimpleMatrix samplesMatrix = new SimpleMatrix(primitiveDoubles); + + double[] weightsArray = new double[queSize]; + Arrays.fill(weightsArray, 1.0); + + SimpleMatrix weightsMatrix = new SimpleMatrix(1, queSize, true, weightsArray); + + double avrg = samplesMatrix.dot(weightsMatrix)/weightsMatrix.elementSum(); + return avrg; + } + + private void updateOdometry() { + PoseEstimate poseEst = LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight"); + + /// Invalidate the vision measurements if some parameters are not met \\\ + if (poseEst == null) { + return; + } + + Pose2d estimatedPose = poseEst.pose; + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + + // If detected tags are not enough + SmartDashboard.putNumber("Tag Count", poseEst.tagCount); + if(poseEst.tagCount < 2 || poseEst.rawFiducials.length <= 0) { + return; + } + + // Make sure that the estimated position is within field bounds. + if ( + estimatedPose.getX() < 0.0 || estimatedPose.getX() > FieldConstants.kFieldWidth + || estimatedPose.getY() < 0.0 || estimatedPose.getY() > FieldConstants.kFieldLength + ) { + return; + } + + double distanceDiff = currentPose.getTranslation().getDistance(estimatedPose.getTranslation()); + if (m_allowJumps == false && distanceDiff > 2.0) { + return; + } + + // Make sure that the abiguity of the primary target is not to high + Double bestTargetAmbiguity = poseEst.rawFiducials[0].ambiguity; + if (bestTargetAmbiguity > 0.2) { + return; + } + + /// Actually do the vision measurements \\\ + + // A Standard Deviation, in the form of [x, y, theta]ᵀ in meters and radians. + double stdDev = 0.5 + bestTargetAmbiguity; + Vector errorVec = VecBuilder.fill(stdDev, stdDev,9999999); + + // Trusting the yaw when disabled to get our robot orientation. + // if (DriverStation.isDisabled()) { + // errorVec = VecBuilder.fill(stdDev, stdDev,0); + // } + + // Add the measurements to the pose estimator + m_poseEstimator.setVisionMeasurementStdDevs(errorVec); + m_poseEstimator.addVisionMeasurement( + estimatedPose, + poseEst.timestampSeconds + ); + + m_YawQue.add(estimatedPose.getRotation().getDegrees()); + } + + @Override + public void periodic() { + updateOdometry(); + } + + public void setAllowJumps(boolean allowJumps) { + m_allowJumps = allowJumps; + } + + public void resetYawQue() { + m_YawQue.clear(); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/AutoCommands.java b/2026-Rebuilt/src/main/java/frc/robot/utils/AutoCommands.java new file mode 100644 index 0000000..0b08198 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/AutoCommands.java @@ -0,0 +1,88 @@ +package frc.robot.utils; + +import java.io.File; +import edu.wpi.first.wpilibj.Filesystem; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +import java.util.ArrayList; +import java.util.Optional; + +import com.pathplanner.lib.commands.PathPlannerAuto; + +// This goes to "/home/lvuser/deploy/pathplanner/autos" and grabs the names of all .auto files +// We are asuming that the PathPlannerAuto(string) is the file name of the auto +// We are asuming that the files are always in the "/home/lvuser/deploy/pathplanner/autos" +// We found this information at https://github.com/mjansen4857/pathplanner/wiki/PathPlannerLib:-Java-Usage +public class AutoCommands { + public static ArrayList getPathNames() { + // Creating an ArrayList so I can fill it with auto file names w/o the extention. + ArrayList pathNames = new ArrayList(); + + // Getting the folder and files. + String deployDirectoryPath = Filesystem.getDeployDirectory().getPath(); + File deployDirectory = new File(deployDirectoryPath+"/pathplanner/paths"); + File[] listOfFiles = deployDirectory.listFiles(); + + // Checking if the files exist + if (listOfFiles != null) { + for (File file : listOfFiles) { + String fullName = file.getName(); // Returns [file_name].[extention] + if (file.isFile()) { + String[] splitName = fullName.split("\\."); // Splitting the file name into seprate parts. + + // If its an auto file then add it to the list. + if (splitName.length > 1 && "path".equals(splitName[1])) { + pathNames.add(splitName[0]); + System.out.println("FOUND PATH: "+splitName[0]); + } + } + } + } + + // Returning the new array + return pathNames; + } + + public static ArrayList getAutoNames() { + // Creating an ArrayList so I can fill it with auto file names w/o the extention. + ArrayList autoNames = new ArrayList(); + + // Getting the folder and files. + String deployDirectoryPath = Filesystem.getDeployDirectory().getPath(); + File deployDirectory = new File(deployDirectoryPath+"/pathplanner/autos"); + File[] listOfFiles = deployDirectory.listFiles(); + + // Checking if the files exist + if (listOfFiles != null) { + for (File file : listOfFiles) { + String fullName = file.getName(); // Returns [file_name].[extention] + if (file.isFile()) { + String[] splitName = fullName.split("\\."); // Splitting the file name into seprate parts. + + // If its an auto file then add it to the list. + if (splitName.length > 1 && "auto".equals(splitName[1])) { + autoNames.add(splitName[0]); + System.out.println("FOUND AUTO: "+splitName[0]); + } + } + } + } + + // Returning the new array + return autoNames; + } + + public static ArrayList getAutoCommands(Optional driveSubsystem) { + ArrayList autoCommands = new ArrayList(); + + if (driveSubsystem.isPresent()) { + ArrayList autoNames = getAutoNames(); + for (String autoName : autoNames) { + autoCommands.add(new PathPlannerAuto(autoName)); + } + } + + return autoCommands; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/ConditionalRotation.java b/2026-Rebuilt/src/main/java/frc/robot/utils/ConditionalRotation.java new file mode 100644 index 0000000..b403bee --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/ConditionalRotation.java @@ -0,0 +1,38 @@ +package frc.robot.utils; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.subsystems.DriveSubsystem; + +public class ConditionalRotation { + /** + * This creates a conditional rotation command, where it only rotates if its outside of the expected error. + * + * @param driveSubsystem The drive subsystem. + * @param poseEstimator The pose estimator. + * @param targetRotation The desired Rotation. + * @param error The maximum accepted error in degrees. + * @param speed The maximum speed the rotation command will go. + */ + public static Command New(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, Rotation2d targetRotation, double error, double speed) { + return Commands.either( + new InstantCommand(), + new RotateToRotation2D( + driveSubsystem, + poseEstimator, + targetRotation, + speed + ), + () -> { + Rotation2d currentRot = poseEstimator.getEstimatedPosition().getRotation(); + double diff = Math.abs((targetRotation.minus(currentRot)).getDegrees()); + + return diff <= error; + } + ); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/HubUtils.java b/2026-Rebuilt/src/main/java/frc/robot/utils/HubUtils.java new file mode 100644 index 0000000..2799b72 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/HubUtils.java @@ -0,0 +1,43 @@ +package frc.robot.utils; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import frc.robot.Constants.FieldConstants; + +import java.util.function.BooleanSupplier; + +public class HubUtils { + private static Pose2d getHubPose(BooleanSupplier isRedSup) { + Boolean isRed = isRedSup.getAsBoolean(); + if (isRed == true) { + return FieldConstants.kRedHubCenter; + } else { + return FieldConstants.kBlueHubCenter; + } + } + + // Returns the rotation the robot needs to face, field relative, to be looking at the hub. + public static Rotation2d getRobotToHubAngle(DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed) { + Pose2d currentPose = poseEstimator.getEstimatedPosition(); + Pose2d hubPose = getHubPose(isRed); + + PoseDiff dPose = new PoseDiff(currentPose, hubPose); + + Rotation2d targetRotation = new Rotation2d(Math.atan2(dPose.y, dPose.x)); + return targetRotation; + } + + // Returns the distance to your team hub in meters. + public static Double getHubDistance(DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed) { + Pose2d currentPose = poseEstimator.getEstimatedPosition(); + Pose2d hubPose = getHubPose(isRed); + + PoseDiff length = new PoseDiff(currentPose, hubPose); + + Double lenSq = Math.pow(length.x, 2) + Math.pow(length.y, 2); + Double len = Math.sqrt(lenSq); + + return len; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/LauncherUtils.java b/2026-Rebuilt/src/main/java/frc/robot/utils/LauncherUtils.java new file mode 100644 index 0000000..6b4ebb6 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/LauncherUtils.java @@ -0,0 +1,86 @@ +package frc.robot.utils; + +import java.util.function.BooleanSupplier; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; + +public class LauncherUtils { + // + // Characterization data for the launcher. This table links shooting + // distance to known-good launcher speeds. Distances are from the centre + // of the robot base to the centre of the hub. + // + private static final double[][] m_DistanceToRPM = { + {2.44, 2470.0}, + {3.12, 2700.0}, + {3.81, 2800.0}, + {5.21, 3300.0}, + }; + + // + // Return the minimum robot-to-hub distance from which we know we + // can score fuel. + // + public static double getMinShootingDistance() { + return m_DistanceToRPM[0][0]; + } + + public static Boolean canScoreFromHere(DifferentialDrivePoseEstimator poseEstimator, BooleanSupplier isRed) { + // How far are we from our alliance hub? + double distance = HubUtils.getHubDistance(poseEstimator, isRed); + + // This doesn't take into consideration which part of the field we are in so will + // report true even in the neutral zone. We assume the driver is aware enough of the + // robot position to know this! + return ((distance >= m_DistanceToRPM[0][0]) && (distance <= m_DistanceToRPM[m_DistanceToRPM.length - 1][0])); + } + + // + // Given a distance between the robot and the hub, calculate the + // launcher flywheel speed needed to successfully score fuel from + // that distance. + // + public static double getFlywheelSpeed(double Distancem) + { + double target = 0.0; + + // Look for cases where we're outside the range from which we can + // successfully score. If we're too close, return the minimum + // speed. + if (Distancem <= m_DistanceToRPM[0][0]) + { + return m_DistanceToRPM[0][1]; + } + + // Are we too far away from the hub to score? If so, return the + // maximum speed. + if (Distancem >= m_DistanceToRPM[m_DistanceToRPM.length - 1][0]) + { + return m_DistanceToRPM[m_DistanceToRPM.length - 1][1]; + } + + // Only scan the table if the current distance is higher than + // the minimum from which we can score. In cases where we're closer + // than this, return the minimum launcher speed. + if (Distancem > m_DistanceToRPM[0][0]) + { + for (int i = 0; i < (m_DistanceToRPM.length - 1); i++) + { + if((Distancem > m_DistanceToRPM[i][0]) && (Distancem <= m_DistanceToRPM[i+1][0])) + { + // We found the interval containing the requested distance so determine + // the required target speed using linear interpolation. + target = m_DistanceToRPM[i][1] + + (((m_DistanceToRPM[i+1][1] - m_DistanceToRPM[i][1]) / + (m_DistanceToRPM[i+1][0] - m_DistanceToRPM[i][0])) * + (Distancem - m_DistanceToRPM[i][0])); + + return target; + } + } + } + + // We should never get here but, if we do, return the maximum speed. + return m_DistanceToRPM[m_DistanceToRPM.length - 1][1]; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/LimitedQueue.java b/2026-Rebuilt/src/main/java/frc/robot/utils/LimitedQueue.java new file mode 100644 index 0000000..cc0a705 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/LimitedQueue.java @@ -0,0 +1,19 @@ +package frc.robot.utils; + +import java.util.LinkedList; + +// An extention of LinkedList that limits the max number of elements when using add and remove. +public class LimitedQueue extends LinkedList { + private int limit; + + public LimitedQueue(int limit) { + this.limit = limit; + } + + @Override + public boolean add(E o) { + super.add(o); + while (size() > limit) { super.remove(); } + return true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/PathUtils.java b/2026-Rebuilt/src/main/java/frc/robot/utils/PathUtils.java new file mode 100644 index 0000000..41f85e3 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/PathUtils.java @@ -0,0 +1,211 @@ +package frc.robot.utils; + +import java.util.ArrayList; +import java.util.List; + +import com.pathplanner.lib.path.GoalEndState; +import com.pathplanner.lib.path.IdealStartingState; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; +import com.pathplanner.lib.path.Waypoint; + +import edu.wpi.first.math.Pair; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; + +public class PathUtils { + public static List deepCopyWaypoints(List base) { + List newWaypoints = new ArrayList<>(); + for (Waypoint w : base) { + Translation2d prev_o = w.prevControl(); + Translation2d anchor_o = w.anchor(); + Translation2d next_o = w.nextControl(); + + Translation2d prev = null; + if (prev_o != null) { + prev = new Translation2d(prev_o.getX(), prev_o.getY()); + } + + Translation2d anchor = null; + if (anchor_o != null) { + anchor = new Translation2d(anchor_o.getX(), anchor_o.getY()); + } + + Translation2d next = null; + if (next_o != null) { + next = new Translation2d(next_o.getX(), next_o.getY()); + } + + newWaypoints.add(new Waypoint(prev, anchor, next)); + } + + return newWaypoints; + } + + /** + * Allows you to do the modification of a path w/o needing to do the jankyness yourself. + * + * @param basePath The path that is used as a base. + * @param pointsToModify A pair that represents the index of a point on the path, and the replacement value. + * @return The path with the modified positions. + */ + @SafeVarargs + public static PathPlannerPath modifyPath(PathPlannerPath basePath, Pair... pointsToModify) { + List pathWaypoints = deepCopyWaypoints(basePath.getWaypoints()); + for (Pair point : pointsToModify) { + Integer index = point.getFirst(); + if (pathWaypoints.size() > index && index >= 0) { + Waypoint oldPoint = pathWaypoints.get(index); + Waypoint newPoint = new Waypoint( + oldPoint.prevControl(), + point.getSecond().getTranslation(), + oldPoint.nextControl() + ); + + pathWaypoints.set(index, newPoint); + } + } + + PathPlannerPath newPath = new PathPlannerPath( + pathWaypoints, + basePath.getGlobalConstraints(), + basePath.getIdealStartingState(), + basePath.getGoalEndState(), + basePath.isReversed() + ); + + return newPath; + } + + /** + * Append a set of points to the base path. + * + * @param basePath The path that is used as a base. + * @param startIndex The index of where to append to, 0 is the start and -1 is the end. + * @param pointsToAppend The poses to append at the location, in order. + * @return The path with the modified positions. + */ + public static PathPlannerPath appendToPath(PathPlannerPath basePath, Integer index, Pose2d pointToAppend) { + List pathWaypoints = deepCopyWaypoints(basePath.getWaypoints()); + + Translation2d newAnchor = pointToAppend.getTranslation(); + Translation2d prevControl = null; + Translation2d nextControl = null; + + int lastWaypointIndex = pathWaypoints.size()-1; + if (index == -1) { + index = lastWaypointIndex; + } + + if (lastWaypointIndex == index) { + Waypoint prev = pathWaypoints.get(index); + prevControl = prev.anchor(); + pathWaypoints.set(index, new Waypoint(prev.prevControl(), prev.anchor(), newAnchor)); + } else if (index == 0) { + Waypoint next = pathWaypoints.get(index); + nextControl = next.anchor(); + pathWaypoints.set(index, new Waypoint(newAnchor, next.anchor(), next.nextControl())); + } else if (index > 0 && index < lastWaypointIndex) { + Waypoint prev = pathWaypoints.get(index-1); + prevControl = prev.anchor(); + pathWaypoints.set(index-1, new Waypoint(prev.prevControl(), prev.anchor(), newAnchor)); + + Waypoint next = pathWaypoints.get(index); + nextControl = next.anchor(); + pathWaypoints.set(index, new Waypoint(newAnchor, next.anchor(), next.nextControl())); + } + + if (pathWaypoints.size() > index) { + Waypoint point = new Waypoint(prevControl, newAnchor, nextControl); + pathWaypoints.add(index, point); + } + + PathPlannerPath newPath = new PathPlannerPath( + pathWaypoints, + basePath.getGlobalConstraints(), + basePath.getIdealStartingState(), + basePath.getGoalEndState(), + basePath.isReversed() + ); + + return newPath; + } + + /** + * This creates a path on the fly to a given target point. + * + * @param startPose The pose the robot will be at the start of the path. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param endVelocity The velocity it should be at the end of the path. + * @param reversed If the robot should run the path in reversed (backward) + */ + public static PathPlannerPath OnTheFlyToTarget(Pose2d startPose, Pose2d targetPose, PathConstraints constraints, Double endVelocity, Boolean reversed) + { + // Create a list of waypoints from poses. Each pose represents one waypoint. + // The rotation component of the pose should be the direction of travel. Do not use holonomic rotation. + Rotation2d rotateMod = Rotation2d.kZero; + if (reversed) { + rotateMod = Rotation2d.k180deg; + } + + List waypoints = PathPlannerPath.waypointsFromPoses( + new Pose2d(startPose.getTranslation(), startPose.getRotation().minus(rotateMod)), + new Pose2d(targetPose.getTranslation(), targetPose.getRotation().minus(rotateMod)) + ); + + // Create the path using the waypoints created above + PathPlannerPath path = new PathPlannerPath( + waypoints, + constraints, + new IdealStartingState(0.0, startPose.getRotation()), // The ideal starting state, this is only relevant for pre-planned paths, so can be null for on-the-fly paths. + new GoalEndState(endVelocity, targetPose.getRotation()), + reversed + ); + + // Prevent the path from being flipped if the coordinates are already correct + path.preventFlipping = true; + + // Create the command version of the path and return it + return path; + } + + /** + * This creates a path on the fly to a given target point. + * + * @param startPose The pose the robot will be at the start of the path. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param endVelocity The velocity it should be at the end of the path. + */ + public static PathPlannerPath OnTheFlyToTarget(Pose2d startPose, Pose2d targetPose, PathConstraints constraints, Double endVelocity) + { + return OnTheFlyToTarget(startPose, targetPose, constraints, endVelocity, false); + } + + /** + * This creates a path on the fly to a given target point. + * + * @param startPose The pose the robot will be at the start of the path. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + * @param reversed If the robot should run the path in reversed (backward) + */ + public static PathPlannerPath OnTheFlyToTarget(Pose2d startPose, Pose2d targetPose, PathConstraints constraints, Boolean reversed) + { + return OnTheFlyToTarget(startPose, targetPose, constraints, 0.0, reversed); + } + + /** + * This creates a path on the fly to a given target point. + * + * @param startPose The pose the robot will be at the start of the path. + * @param targetPose The pose the robot will make a point to go to. + * @param constraints The constraints the robot will follow while following the path. + */ + public static PathPlannerPath OnTheFlyToTarget(Pose2d startPose, Pose2d targetPose, PathConstraints constraints) + { + return OnTheFlyToTarget(startPose, targetPose, constraints, 0.0, false); + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/Pigeon.java b/2026-Rebuilt/src/main/java/frc/robot/utils/Pigeon.java new file mode 100644 index 0000000..1c0ed71 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/Pigeon.java @@ -0,0 +1,140 @@ +// Taken and modified from CTRE's example +// https://github.com/CrossTheRoadElec/Phoenix5-Examples/blob/d70cab6060617bbed5e207c2eaf8747af09a15f6/Java%20General/Pigeon2/src/main/java/frc/robot/subsystems/PigeonSubsystem.java +package frc.robot.utils; + +import com.ctre.phoenix6.configs.Pigeon2Configuration; +import com.ctre.phoenix6.hardware.Pigeon2; + +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.DriverStation; +import frc.robot.Constants.RobotConstants; + +public class Pigeon { + private final Pigeon2 m_pigeon2; + private boolean m_exists; + private double m_yawOffset = 0; + + private boolean m_isAccurate = false; + + public Pigeon(int PigeonCanId) { + m_pigeon2 = new Pigeon2(PigeonCanId); + m_exists = m_pigeon2.isConnected(); + + if (m_exists == false) { + DriverStation.reportWarning("Pigeon on canid2 does not exist! Default values will be returned.", true); + } else { + Pigeon2Configuration config = new Pigeon2Configuration(); + config.MountPose.MountPoseYaw = RobotConstants.kPigeonYawOffset; + config.MountPose.MountPosePitch = RobotConstants.kPigeonPitchOffset; + config.MountPose.MountPoseRoll = RobotConstants.kPigeonRollOffset; + m_pigeon2.getConfigurator().apply(config); + + m_pigeon2.reset(); + } + } + + private void dosntExist() { + if (RobotConstants.kDoPigeonWarn) { + DriverStation.reportWarning("Pigeon on canid 2 does not exist, returning default value!", false); + } + } + + public void reset() { + if (m_exists) { + m_pigeon2.reset(); + } else { + dosntExist(); + } + } + + public Rotation2d getRotation2d() { + if (m_exists) { + Rotation2d readRotation = m_pigeon2.getRotation2d(); + Rotation2d offset = new Rotation2d(m_yawOffset * (Math.PI/180)); + Rotation2d newRotation = readRotation.rotateBy(offset); + return newRotation; + } else { + dosntExist(); + return Rotation2d.kZero; + } + } + + public double getYaw() { + if (m_exists) { + return m_pigeon2.getYaw().getValueAsDouble() + m_yawOffset; + } else { + dosntExist(); + return 0.0; + } + } + + // I dont think offset is needed for this. + public double getYawRate() { + if (m_exists) { + return m_pigeon2.getAngularVelocityZWorld().getValueAsDouble(); + } else { + dosntExist(); + return 0.0; + } + } + + public double getPitch() { + if (m_exists) { + return m_pigeon2.getPitch().getValueAsDouble(); + } else { + dosntExist(); + return 0.0; + } + } + + public double getRoll() { + if (m_exists) { + return m_pigeon2.getRoll().getValueAsDouble(); + } else { + dosntExist(); + return 0.0; + } + } + + public void setYaw(double yaw) { + if (m_exists) { + m_pigeon2.setYaw(yaw-m_yawOffset); + } else { + dosntExist(); + } + } + + public double getUpTime() { + if (m_exists) { + return m_pigeon2.getUpTime().getValueAsDouble(); + } else { + dosntExist(); + return -1; + } + } + + public double getTemp() { + if (m_exists) { + return m_pigeon2.getTemperature().getValueAsDouble(); + } else { + dosntExist(); + return -1; + } + } + + public void setYawOffset(double degrees) { + m_yawOffset = degrees; + } + + public double getYawOffset() { + return m_yawOffset; + } + + public boolean isAccurate() { + return m_isAccurate; + } + + public void setAccuracy(boolean isAccurate) { + m_isAccurate = isAccurate; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/PneumaticHubWrapper.java b/2026-Rebuilt/src/main/java/frc/robot/utils/PneumaticHubWrapper.java new file mode 100644 index 0000000..ea5b166 --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/PneumaticHubWrapper.java @@ -0,0 +1,39 @@ +// +// A trivial class that wraps PneumaticHub to allow us to throw an exception +// if the device doesn't exist on the CAN bus. +// +package frc.robot.utils; + +import edu.wpi.first.wpilibj.PneumaticHub; +import edu.wpi.first.hal.REVPHVersion; + +public class PneumaticHubWrapper extends PneumaticHub { + private PneumaticHub m_Hub; + + /* @throws Exception */ + public PneumaticHubWrapper() throws Exception { + m_Hub = new PneumaticHub(); + + if (!isAvailable()) + { + throw new Exception("Pneumatic hub is not connected or is malfunctioning."); + } + } + + /* @throws Exception */ + public PneumaticHubWrapper(int module) throws Exception { + m_Hub = new PneumaticHub(module); + + if (!isAvailable()) + { + throw new Exception("Pneumatic hub is not connected or is malfunctioning."); + } + } + + public boolean isAvailable() + { + REVPHVersion Version = m_Hub.getVersion(); + + return (Version.firmwareMajor == 0) ? false : true; + } +} diff --git a/2026-Rebuilt/src/main/java/frc/robot/utils/PoseDiff.java b/2026-Rebuilt/src/main/java/frc/robot/utils/PoseDiff.java new file mode 100644 index 0000000..db1750a --- /dev/null +++ b/2026-Rebuilt/src/main/java/frc/robot/utils/PoseDiff.java @@ -0,0 +1,14 @@ +package frc.robot.utils; + +import edu.wpi.first.math.geometry.Pose2d; + +public class PoseDiff { + public final double x; + public final double y; + + // pose2 - pose 1 + public PoseDiff(Pose2d pose1, Pose2d pose2) { + this.x = pose2.getX() - pose1.getX(); + this.y = pose2.getY() - pose1.getY(); + } +} \ No newline at end of file diff --git a/2026-Rebuilt/testmode.json b/2026-Rebuilt/testmode.json new file mode 100644 index 0000000..598ac72 --- /dev/null +++ b/2026-Rebuilt/testmode.json @@ -0,0 +1,200 @@ +{ + "version": 1.0, + "grid_size": 128, + "tabs": [ + { + "name": "Toaster Testing", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Drive Commands", + "x": 0.0, + "y": 0.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Drive Commands", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Auto Chooser", + "x": 0.0, + "y": 128.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Auto Chooser", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Field", + "x": 1280.0, + "y": 0.0, + "width": 640.0, + "height": 640.0, + "type": "Field", + "properties": { + "topic": "/SmartDashboard/Field", + "period": 0.06, + "field_game": "Rebuilt", + "robot_width": 0.85, + "robot_length": 0.85, + "show_other_objects": true, + "show_trajectories": true, + "field_rotation": 0.0, + "robot_color": 4294198070, + "trajectory_color": 4294967295, + "show_robot_outside_widget": true + } + }, + { + "title": "Pose X (Meter)", + "x": 1536.0, + "y": 640.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose X (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Y (Meter)", + "x": 1664.0, + "y": 640.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Y (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Theta (Degrees)", + "x": 1792.0, + "y": 640.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Theta (Degrees)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "limelight", + "x": 512.0, + "y": 0.0, + "width": 768.0, + "height": 768.0, + "type": "Camera Stream", + "properties": { + "topic": "/CameraPublisher/limelight", + "period": 0.06, + "rotation_turns": 0 + } + }, + { + "title": "Left Speed (MPS)", + "x": 256.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Right Speed (MPS)", + "x": 384.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Left Target (MPS)", + "x": 256.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Right Target (MPS)", + "x": 384.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Hub Distance", + "x": 384.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Hub Distance", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "mt2 Tag Count", + "x": 256.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/mt2 Tag Count", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/2026-Rebuilt/todo_stuff.txt b/2026-Rebuilt/todo_stuff.txt new file mode 100644 index 0000000..ec5f152 --- /dev/null +++ b/2026-Rebuilt/todo_stuff.txt @@ -0,0 +1,18 @@ +Depo Side Auto: +1. Deadreackon forward for x meters (over bump) +2. Relocate +3. DSA_PickupFwd +4. Face opponent wall +5. DSA_PickupBwd +6. Deadreckon backward for x meters (over bump) +7. Relocate +8. DSA_ParkAtWall +9. Aim & Shoot + +10A. Drive to Bump +11A. Deadreckon Forward +12A. Relocate +13A. Drive to balls + +10B. Pickup Depo +11B. Aim & Shoot \ No newline at end of file diff --git a/2026-Rebuilt/user_manual/Robot-Manual-2026.pdf b/2026-Rebuilt/user_manual/Robot-Manual-2026.pdf new file mode 100644 index 0000000..e201d89 Binary files /dev/null and b/2026-Rebuilt/user_manual/Robot-Manual-2026.pdf differ diff --git a/2026-Rebuilt/vendordeps/PathplannerLib-2026.1.2.json b/2026-Rebuilt/vendordeps/PathplannerLib-2026.1.2.json new file mode 100644 index 0000000..f72fa41 --- /dev/null +++ b/2026-Rebuilt/vendordeps/PathplannerLib-2026.1.2.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2026.1.2.json", + "name": "PathplannerLib", + "version": "2026.1.2", + "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", + "frcYear": "2026", + "mavenUrls": [ + "https://3015rangerrobotics.github.io/pathplannerlib/repo" + ], + "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", + "javaDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-java", + "version": "2026.1.2" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2026.1.2", + "libName": "PathplannerLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} \ No newline at end of file diff --git a/2026-Rebuilt/vendordeps/Studica.json b/2026-Rebuilt/vendordeps/Studica.json new file mode 100644 index 0000000..b51bf58 --- /dev/null +++ b/2026-Rebuilt/vendordeps/Studica.json @@ -0,0 +1,71 @@ +{ + "fileName": "Studica.json", + "name": "Studica", + "version": "2026.0.0", + "frcYear": "2026", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "mavenUrls": [ + "https://dev.studica.com/maven/release/2026/" + ], + "jsonUrl": "https://dev.studica.com/maven/release/2026/json/Studica-2026.0.0.json", + "javaDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-java", + "version": "2026.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-driver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-cpp", + "version": "2026.0.0", + "libName": "Studica", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.studica.frc", + "artifactId": "Studica-driver", + "version": "2026.0.0", + "libName": "StudicaDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/.SysId/sysid.json b/prototype_projects/Experimental_Drivetrain_2025/.SysId/sysid.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.SysId/sysid.json @@ -0,0 +1 @@ +{} diff --git a/prototype_projects/Experimental_Drivetrain_2025/.github/workflows/ci.yml b/prototype_projects/Experimental_Drivetrain_2025/.github/workflows/ci.yml new file mode 100644 index 0000000..28e5370 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +# This YML defines the basic build CI job. For syntax, see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# This workflow file is based on this WPILib Reference with modifications: https://docs.wpilib.org/en/stable/docs/software/advanced-gradlerio/robot-code-ci.html +name: robot_ci + +# This action runs on all pull requests into main and when the result of the pull request is merged. +on: + pull_request: + branches: + - main + push: + branches: + - main + +# The concurrency setting prevents multiple runs of this workflow on the same ref/hash +concurrency: ${{ github.workflow }}-${{ github.ref }} + +# We define one job called 'ci' which runs the gradle build. A separate ruleset in the repository will make this a commit gate by requiring the job to pass +# In the job, we tell the orchestrator to run on a github provided ubuntu 22.04 instance and to use the WPILib provided docker container +# Using that docker container makes the build look like a developer machine install with WPILib available +# The job then runs gradlew build +# If the project structure significantly changes (e.g. gradlew moves) then the path below for both the chmod and the gradlew invocation need to match the change. +jobs: + ci: + runs-on: ubuntu-22.04 + container: + image: docker://wpilib/roborio-cross-ubuntu:2025-22.04 + steps: + - name: Git Checkout + uses: actions/checkout@v4 + - name: Git Clone Common Repo + run: | + sh common-repo-sync.sh + - name: Gradle Build + run: | + chmod +x gradlew + ./gradlew build \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/.gitignore b/prototype_projects/Experimental_Drivetrain_2025/.gitignore new file mode 100644 index 0000000..8c93450 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.gitignore @@ -0,0 +1,193 @@ +## Additional .gitingore file for the project defined by FRC 9577 +.gradle-home/ + +## This is the location of our common repo. +src/main/java/frc/recoil/ + +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/Experimental_Drivetrain_2025/.vscode/launch.json b/prototype_projects/Experimental_Drivetrain_2025/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/.vscode/settings.json b/prototype_projects/Experimental_Drivetrain_2025/.vscode/settings.json new file mode 100644 index 0000000..612cdd0 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/.wpilib/wpilib_preferences.json b/prototype_projects/Experimental_Drivetrain_2025/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a792267 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2025", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/LICENSE b/prototype_projects/Experimental_Drivetrain_2025/LICENSE new file mode 100644 index 0000000..66c3dbc --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Recoil Robotics + +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. diff --git a/prototype_projects/Experimental_Drivetrain_2025/Makefile b/prototype_projects/Experimental_Drivetrain_2025/Makefile new file mode 100644 index 0000000..4fbddeb --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/Makefile @@ -0,0 +1,22 @@ +WORKDIR ?= $(shell pwd) + +WPILIB_CONTAINER := wpilib/roborio-cross-ubuntu:2025-22.04 + +.PHONY: build +build: + bash common-repo-sync.sh + docker run -it -v ${WORKDIR}:/work -w /work -e GRADLE_USER_HOME=/work/.gradle-home ${WPILIB_CONTAINER} sh -c "./gradlew build" + +.PHONY: deploy +deploy: + docker run -it -v ${WORKDIR}:/work -w /work -e GRADLE_USER_HOME=/work/.gradle-home ${WPILIB_CONTAINER} sh -c "./gradlew deploy" + +.PHONY: clean +clean: + docker run -it -v ${WORKDIR}:/work -w /work -e GRADLE_USER_HOME=/work/.gradle-home ${WPILIB_CONTAINER} sh -c "rm -Rf build" + +.PHONY: deep-clean +deep-clean: clean + docker run -it -v ${WORKDIR}:/work -w /work -e GRADLE_USER_HOME=/work/.gradle-home ${WPILIB_CONTAINER} sh -c "rm -Rf .gradle && rm -Rf .gradle-home" + rm -Rf src/main/java/frc/recoil + git clean -fdx \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/README.md b/prototype_projects/Experimental_Drivetrain_2025/README.md new file mode 100644 index 0000000..426720e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/README.md @@ -0,0 +1,42 @@ +# robot-base +FRC 9577's Template Repository for Robot Code + +# How to use this template +This repositry is a github template repository. It represents the baseline code that FRC 9577 uses for our robots, including dependencies we use in our designs. To use our code, create a repository using this one as a template. + +## Repository Approach +The approach here is to minimize setup time for a developer. By leveraging the WPILib distributed docker container and through effective use of docker we limit the prerequisite install to just a few items. This approach is for development only and does not replace the driver station. We support both linux and Windows for development. (Currently ) + +## Installing Prerequsiites +This repository does not contain any tools. You need to install four tools to support your build: git, VSCode, make, and docker. + +### Installing on Linux +To install on linux, use your package manager to pick up git, make, and docker. Testing was done on git 2.43, make 4.3, and docker 27.3.1. + +Both make and git come directly from the package manager (e.g. for ubuntu `apt install make git`). + +VS Code is installation instructions are here: https://code.visualstudio.com/docs/setup/linux + +Docker installation instructions are here: https://docs.docker.com/engine/install/ubuntu/ + +### Installing on Windows +Herein, we provide references to each of the windows installers:https://gnuwin32.sourceforge.net/packages/make.htmz +* git: https://git-scm.com/downloads/win +* make: https://gnuwin32.sourceforge.net/packages/make.htm +* docker: https://docs.docker.com/desktop/install/windows-install/ +* VSCode: https://code.visualstudio.com/docs/setup/windows + +This is more manual than a linux install, but it's just 4 tools. + +When installing git, choose the option to install bash. Always use bash as your windows terminal when using this repo, as we only test on bash. + +### Windows "funnies" +- Need to install make for everyone and add the make path manually to system paths. +- Need to build via external git bash terminal started as administrator. + +# Updating this template +When a new version of tools or libraries becomes available, use the WPILIB VSCode plugin to update the files in the project. Then, open a pull request against main to merge your change in. + +Sometimes there are updates not serviced by the WPILib plugin. In these cases, make the changes using editors and push the results to github. + +This repository can also be forked. diff --git a/prototype_projects/Experimental_Drivetrain_2025/WPILib-License.md b/prototype_projects/Experimental_Drivetrain_2025/WPILib-License.md new file mode 100644 index 0000000..645e542 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2024 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/prototype_projects/Experimental_Drivetrain_2025/build.gradle b/prototype_projects/Experimental_Drivetrain_2025/build.gradle new file mode 100644 index 0000000..bb54b03 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/build.gradle @@ -0,0 +1,104 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2025.2.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = true // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +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 + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from sourceSets.main.allSource + 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/prototype_projects/Experimental_Drivetrain_2025/common-repo-sync.sh b/prototype_projects/Experimental_Drivetrain_2025/common-repo-sync.sh new file mode 100644 index 0000000..fc3b951 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/common-repo-sync.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +git config --global --add safe.directory /home/jeff/work/robot-base/src/main/java/frc/recoil +if [ -d "src/main/java/frc/recoil" ]; then + (cd src/main/java/frc/recoil && git pull) +else + git clone --branch main --depth 1 https://github.com/frc9577/common src/main/java/frc/recoil +fi diff --git a/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/Experimental_Drivetrain_2025/gradlew b/prototype_projects/Experimental_Drivetrain_2025/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/Experimental_Drivetrain_2025/gradlew.bat b/prototype_projects/Experimental_Drivetrain_2025/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/Experimental_Drivetrain_2025/settings.gradle b/prototype_projects/Experimental_Drivetrain_2025/settings.gradle new file mode 100644 index 0000000..969c7b0 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2025' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name 'frcHome' + url frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/Experimental_Drivetrain_2025/simgui-ds.json b/prototype_projects/Experimental_Drivetrain_2025/simgui-ds.json new file mode 100644 index 0000000..73cc713 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/simgui-ds.json @@ -0,0 +1,92 @@ +{ + "keyboardJoysticks": [ + { + "axisConfig": [ + { + "decKey": 65, + "incKey": 68 + }, + { + "decKey": 87, + "incKey": 83 + }, + { + "decKey": 69, + "decayRate": 0.0, + "incKey": 82, + "keyRate": 0.009999999776482582 + } + ], + "axisCount": 3, + "buttonCount": 4, + "buttonKeys": [ + 90, + 88, + 67, + 86 + ], + "povConfig": [ + { + "key0": 328, + "key135": 323, + "key180": 322, + "key225": 321, + "key270": 324, + "key315": 327, + "key45": 329, + "key90": 326 + } + ], + "povCount": 1 + }, + { + "axisConfig": [ + { + "decKey": 74, + "incKey": 76 + }, + { + "decKey": 73, + "incKey": 75 + } + ], + "axisCount": 2, + "buttonCount": 4, + "buttonKeys": [ + 77, + 44, + 46, + 47 + ], + "povCount": 0 + }, + { + "axisConfig": [ + { + "decKey": 263, + "incKey": 262 + }, + { + "decKey": 265, + "incKey": 264 + } + ], + "axisCount": 2, + "buttonCount": 6, + "buttonKeys": [ + 260, + 268, + 266, + 261, + 269, + 267 + ], + "povCount": 0 + }, + { + "axisCount": 0, + "buttonCount": 0, + "povCount": 0 + } + ] +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/example.txt b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/autos/SampleAuto.auto b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/autos/SampleAuto.auto new file mode 100644 index 0000000..70b7ab2 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/autos/SampleAuto.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "Example Path" + } + } + ] + } + }, + "resetOdom": true, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/navgrid.json b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/navgrid.json new file mode 100644 index 0000000..23e0db9 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/navgrid.json @@ -0,0 +1 @@ +{"field_size":{"x":17.548,"y":8.052},"nodeSizeMeters":0.3,"grid":[[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]]} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/paths/Example Path.path b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/paths/Example Path.path new file mode 100644 index 0000000..d474f95 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/paths/Example Path.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.1486729452054796, + "y": 6.48169948630137 + }, + "prevControl": null, + "nextControl": { + "x": 2.3986729452054796, + "y": 6.48169948630137 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 5.213912671232877, + "y": 6.48169948630137 + }, + "prevControl": { + "x": 4.963912671232877, + "y": 6.48169948630137 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/settings.json b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/settings.json new file mode 100644 index 0000000..75d95ab --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/deploy/pathplanner/settings.json @@ -0,0 +1,34 @@ +{ + "robotWidth": 0.9, + "robotLength": 0.9, + "holonomicMode": false, + "pathFolders": [ + "New Folder" + ], + "autoFolders": [], + "defaultMaxVel": 3.0, + "defaultMaxAccel": 3.0, + "defaultMaxAngVel": 540.0, + "defaultMaxAngAccel": 720.0, + "defaultNominalVoltage": 12.0, + "robotMass": 74.088, + "robotMOI": 6.883, + "robotTrackwidth": 0.546, + "driveWheelRadius": 0.0508, + "driveGearing": 5.143, + "maxDriveSpeed": 5.45, + "driveMotorType": "krakenX60", + "driveCurrentLimit": 60.0, + "wheelCOF": 1.0, + "flModuleX": 0.273, + "flModuleY": 0.273, + "frModuleX": 0.273, + "frModuleY": -0.273, + "blModuleX": -0.273, + "blModuleY": 0.273, + "brModuleX": -0.273, + "brModuleY": -0.273, + "bumperOffsetX": 0.0, + "bumperOffsetY": 0.0, + "robotFeatures": [] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Constants.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..5a4561c --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Constants.java @@ -0,0 +1,91 @@ +// 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 frc.robot; + +import com.pathplanner.lib.config.ModuleConfig; +import com.pathplanner.lib.config.RobotConfig; + +import edu.wpi.first.math.system.plant.DCMotor; + +/** + * 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 class AutoConstants { + // Module Config Stuff + public static final double kMaxDriveVelocityMPS = 40.0; // This is what SysID is saying i dont think its going 90 mph + public static final double kWheelCOF = 1.0; + public static final int kNumMotors = 4; + public static final double kDriveCurrentLimit = 10.0; + + public static final DCMotor kDriveMotor = DCMotor.getKrakenX60(kNumMotors); + public static final ModuleConfig kMoudleConfig = new ModuleConfig( + DrivetrainConstants.kWheelRadiusMeters, + kMaxDriveVelocityMPS, + kWheelCOF, + kDriveMotor, + kDriveCurrentLimit, + kNumMotors + ); + + // Robot Config Stuff + // TODO: Run SYS ID and fill in! + public static final double kMassKG = 15.0; + public static final double kMOI = kMassKG * (DrivetrainConstants.trackWidthMeters/2) * (DrivetrainConstants.kA_angular / DrivetrainConstants.kA_linear); // Moment of Intertia + + public static final RobotConfig kRobotConfig = new RobotConfig(kMassKG, kMOI, kMoudleConfig, DrivetrainConstants.trackWidthMeters); + } + + public static class OperatorConstants { + public static final int kDriverControllerPort = 0; + } + + public static class DrivetrainConstants { + public static final int kLeftMotorCANID = 10; + public static final int kOptionalLeftMotorCANID = 11; + + public static final int kRightMotorCANID = 20; + public static final int kOptionalRightMotorCANID = 21; + + public static final double kTurnDivider = 2; + public static final double kSpeedDivider = 2.5; + + // These numbers came from sys id program + public static final double kV = 1.8747; // Add x V output to overcome static friction + public static final double kS = 0.15316; // A velocity target of 1 rps results in xV output + public static final double kP = 2.7562; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kA_linear = 0.1141; // Voltage needed to induce a given accel. in the motor shaft + public static final double kA_angular = 0.1141; // TODO: We need to measure this! + public static final double PeakVoltage = 10.0; + + public static final int maxVelocity = 30; // rps/s + public static final int maxAcceleration = 50; // rps + + // Constants used by differential drive command. + public static final double maxVelocityMPS = 1.0; + + // For Auto Potentially + public static boolean kLeftPositiveMovesForward = true; + public static boolean kRightPositiveMovesForward = true; + + // Physical measurements related to the drivetrain. + public static final double kDrivetrainGearRatio = 0.2; + public static final double kWheelRadiusMeters = (4.0 / 2.0) * 0.0254; // Four Inch Wheels + public static final double kWheelCircumference = 2 * Math.PI * DrivetrainConstants.kWheelRadiusMeters; + + // SmartDashboard update frequency for drive subsystem state in 20ms counts. + public static final int kTicksPerUpdate = 5; + + // The track width in meters. + public static final double trackWidthMeters = 29.0 * 0.0254; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/LimelightHelpers.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/LimelightHelpers.java new file mode 100644 index 0000000..1dbf387 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/LimelightHelpers.java @@ -0,0 +1,1645 @@ +//LimelightHelpers v1.11 (REQUIRES LLOS 2025.0 OR LATER) + +package frc.robot; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.DoubleArrayEntry; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.TimestampedDoubleArray; + +/** + * LimelightHelpers provides static methods and classes for interfacing with Limelight vision cameras in FRC. + * This library supports all Limelight features including AprilTag tracking, Neural Networks, and standard color/retroreflective tracking. + */ +public class LimelightHelpers { + + private static final Map doubleArrayEntries = new ConcurrentHashMap<>(); + + /** + * Represents a Color/Retroreflective Target Result extracted from JSON Output + */ + public static class LimelightTarget_Retro { + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Retro() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + + } + + /** + * Represents an AprilTag/Fiducial Target Result extracted from JSON Output + */ + public static class LimelightTarget_Fiducial { + + @JsonProperty("fID") + public double fiducialID; + + @JsonProperty("fam") + public String fiducialFamily; + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Fiducial() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + } + + /** + * Represents a Barcode Target Result extracted from JSON Output + */ + public static class LimelightTarget_Barcode { + + /** + * Barcode family type (e.g. "QR", "DataMatrix", etc.) + */ + @JsonProperty("fam") + public String family; + + /** + * Gets the decoded data content of the barcode + */ + @JsonProperty("data") + public String data; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("pts") + public double[][] corners; + + public LimelightTarget_Barcode() { + } + + public String getFamily() { + return family; + } + } + + /** + * Represents a Neural Classifier Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Classifier { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("zone") + public double zone; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("typ") + public double ty_pixels; + + public LimelightTarget_Classifier() { + } + } + + /** + * Represents a Neural Detector Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Detector { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + public LimelightTarget_Detector() { + } + } + + /** + * Limelight Results object, parsed from a Limelight's JSON results output. + */ + public static class LimelightResults { + + public String error; + + @JsonProperty("pID") + public double pipelineID; + + @JsonProperty("tl") + public double latency_pipeline; + + @JsonProperty("cl") + public double latency_capture; + + public double latency_jsonParse; + + @JsonProperty("ts") + public double timestamp_LIMELIGHT_publish; + + @JsonProperty("ts_rio") + public double timestamp_RIOFPGA_capture; + + @JsonProperty("v") + @JsonFormat(shape = Shape.NUMBER) + public boolean valid; + + @JsonProperty("botpose") + public double[] botpose; + + @JsonProperty("botpose_wpired") + public double[] botpose_wpired; + + @JsonProperty("botpose_wpiblue") + public double[] botpose_wpiblue; + + @JsonProperty("botpose_tagcount") + public double botpose_tagcount; + + @JsonProperty("botpose_span") + public double botpose_span; + + @JsonProperty("botpose_avgdist") + public double botpose_avgdist; + + @JsonProperty("botpose_avgarea") + public double botpose_avgarea; + + @JsonProperty("t6c_rs") + public double[] camerapose_robotspace; + + public Pose3d getBotPose3d() { + return toPose3D(botpose); + } + + public Pose3d getBotPose3d_wpiRed() { + return toPose3D(botpose_wpired); + } + + public Pose3d getBotPose3d_wpiBlue() { + return toPose3D(botpose_wpiblue); + } + + public Pose2d getBotPose2d() { + return toPose2D(botpose); + } + + public Pose2d getBotPose2d_wpiRed() { + return toPose2D(botpose_wpired); + } + + public Pose2d getBotPose2d_wpiBlue() { + return toPose2D(botpose_wpiblue); + } + + @JsonProperty("Retro") + public LimelightTarget_Retro[] targets_Retro; + + @JsonProperty("Fiducial") + public LimelightTarget_Fiducial[] targets_Fiducials; + + @JsonProperty("Classifier") + public LimelightTarget_Classifier[] targets_Classifier; + + @JsonProperty("Detector") + public LimelightTarget_Detector[] targets_Detector; + + @JsonProperty("Barcode") + public LimelightTarget_Barcode[] targets_Barcode; + + public LimelightResults() { + botpose = new double[6]; + botpose_wpired = new double[6]; + botpose_wpiblue = new double[6]; + camerapose_robotspace = new double[6]; + targets_Retro = new LimelightTarget_Retro[0]; + targets_Fiducials = new LimelightTarget_Fiducial[0]; + targets_Classifier = new LimelightTarget_Classifier[0]; + targets_Detector = new LimelightTarget_Detector[0]; + targets_Barcode = new LimelightTarget_Barcode[0]; + + } + + + } + + /** + * Represents a Limelight Raw Fiducial result from Limelight's NetworkTables output. + */ + public static class RawFiducial { + public int id = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double distToCamera = 0; + public double distToRobot = 0; + public double ambiguity = 0; + + + public RawFiducial(int id, double txnc, double tync, double ta, double distToCamera, double distToRobot, double ambiguity) { + this.id = id; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.distToCamera = distToCamera; + this.distToRobot = distToRobot; + this.ambiguity = ambiguity; + } + } + + /** + * Represents a Limelight Raw Neural Detector result from Limelight's NetworkTables output. + */ + public static class RawDetection { + public int classId = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double corner0_X = 0; + public double corner0_Y = 0; + public double corner1_X = 0; + public double corner1_Y = 0; + public double corner2_X = 0; + public double corner2_Y = 0; + public double corner3_X = 0; + public double corner3_Y = 0; + + + public RawDetection(int classId, double txnc, double tync, double ta, + double corner0_X, double corner0_Y, + double corner1_X, double corner1_Y, + double corner2_X, double corner2_Y, + double corner3_X, double corner3_Y ) { + this.classId = classId; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.corner0_X = corner0_X; + this.corner0_Y = corner0_Y; + this.corner1_X = corner1_X; + this.corner1_Y = corner1_Y; + this.corner2_X = corner2_X; + this.corner2_Y = corner2_Y; + this.corner3_X = corner3_X; + this.corner3_Y = corner3_Y; + } + } + + /** + * Represents a 3D Pose Estimate. + */ + public static class PoseEstimate { + public Pose2d pose; + public double timestampSeconds; + public double latency; + public int tagCount; + public double tagSpan; + public double avgTagDist; + public double avgTagArea; + + public RawFiducial[] rawFiducials; + public boolean isMegaTag2; + + /** + * Instantiates a PoseEstimate object with default values + */ + public PoseEstimate() { + this.pose = new Pose2d(); + this.timestampSeconds = 0; + this.latency = 0; + this.tagCount = 0; + this.tagSpan = 0; + this.avgTagDist = 0; + this.avgTagArea = 0; + this.rawFiducials = new RawFiducial[]{}; + this.isMegaTag2 = false; + } + + public PoseEstimate(Pose2d pose, double timestampSeconds, double latency, + int tagCount, double tagSpan, double avgTagDist, + double avgTagArea, RawFiducial[] rawFiducials, boolean isMegaTag2) { + + this.pose = pose; + this.timestampSeconds = timestampSeconds; + this.latency = latency; + this.tagCount = tagCount; + this.tagSpan = tagSpan; + this.avgTagDist = avgTagDist; + this.avgTagArea = avgTagArea; + this.rawFiducials = rawFiducials; + this.isMegaTag2 = isMegaTag2; + } + + } + + /** + * Encapsulates the state of an internal Limelight IMU. + */ + public static class IMUData { + public double robotYaw = 0.0; + public double Roll = 0.0; + public double Pitch = 0.0; + public double Yaw = 0.0; + public double gyroX = 0.0; + public double gyroY = 0.0; + public double gyroZ = 0.0; + public double accelX = 0.0; + public double accelY = 0.0; + public double accelZ = 0.0; + + public IMUData() {} + + public IMUData(double[] imuData) { + if (imuData != null && imuData.length >= 10) { + this.robotYaw = imuData[0]; + this.Roll = imuData[1]; + this.Pitch = imuData[2]; + this.Yaw = imuData[3]; + this.gyroX = imuData[4]; + this.gyroY = imuData[5]; + this.gyroZ = imuData[6]; + this.accelX = imuData[7]; + this.accelY = imuData[8]; + this.accelZ = imuData[9]; + } + } + } + + + private static ObjectMapper mapper; + + /** + * Print JSON Parse time to the console in milliseconds + */ + static boolean profileJSON = false; + + static final String sanitizeName(String name) { + if (name == "" || name == null) { + return "limelight"; + } + return name; + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose3d object. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose3d object representing the pose, or empty Pose3d if invalid data + */ + public static Pose3d toPose3D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 3D Pose Data!"); + return new Pose3d(); + } + return new Pose3d( + new Translation3d(inData[0], inData[1], inData[2]), + new Rotation3d(Units.degreesToRadians(inData[3]), Units.degreesToRadians(inData[4]), + Units.degreesToRadians(inData[5]))); + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose2d object. + * Uses only x, y, and yaw components, ignoring z, roll, and pitch. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose2d object representing the pose, or empty Pose2d if invalid data + */ + public static Pose2d toPose2D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 2D Pose Data!"); + return new Pose2d(); + } + Translation2d tran2d = new Translation2d(inData[0], inData[1]); + Rotation2d r2d = new Rotation2d(Units.degreesToRadians(inData[5])); + return new Pose2d(tran2d, r2d); + } + + /** + * Converts a Pose3d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * + * @param pose The Pose3d object to convert + * @return A 6-element array containing [x, y, z, roll, pitch, yaw] + */ + public static double[] pose3dToArray(Pose3d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = pose.getTranslation().getZ(); + result[3] = Units.radiansToDegrees(pose.getRotation().getX()); + result[4] = Units.radiansToDegrees(pose.getRotation().getY()); + result[5] = Units.radiansToDegrees(pose.getRotation().getZ()); + return result; + } + + /** + * Converts a Pose2d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * Note: z, roll, and pitch will be 0 since Pose2d only contains x, y, and yaw. + * + * @param pose The Pose2d object to convert + * @return A 6-element array containing [x, y, 0, 0, 0, yaw] + */ + public static double[] pose2dToArray(Pose2d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = 0; + result[3] = Units.radiansToDegrees(0); + result[4] = Units.radiansToDegrees(0); + result[5] = Units.radiansToDegrees(pose.getRotation().getRadians()); + return result; + } + + private static double extractArrayEntry(double[] inData, int position){ + if(inData.length < position+1) + { + return 0; + } + return inData[position]; + } + + private static PoseEstimate getBotPoseEstimate(String limelightName, String entryName, boolean isMegaTag2) { + DoubleArrayEntry poseEntry = LimelightHelpers.getLimelightDoubleArrayEntry(limelightName, entryName); + + TimestampedDoubleArray tsValue = poseEntry.getAtomic(); + double[] poseArray = tsValue.value; + long timestamp = tsValue.timestamp; + + if (poseArray.length == 0) { + // Handle the case where no data is available + return null; // or some default PoseEstimate + } + + var pose = toPose2D(poseArray); + double latency = extractArrayEntry(poseArray, 6); + int tagCount = (int)extractArrayEntry(poseArray, 7); + double tagSpan = extractArrayEntry(poseArray, 8); + double tagDist = extractArrayEntry(poseArray, 9); + double tagArea = extractArrayEntry(poseArray, 10); + + // Convert server timestamp from microseconds to seconds and adjust for latency + double adjustedTimestamp = (timestamp / 1000000.0) - (latency / 1000.0); + + RawFiducial[] rawFiducials = new RawFiducial[tagCount]; + int valsPerFiducial = 7; + int expectedTotalVals = 11 + valsPerFiducial * tagCount; + + if (poseArray.length != expectedTotalVals) { + // Don't populate fiducials + } else { + for(int i = 0; i < tagCount; i++) { + int baseIndex = 11 + (i * valsPerFiducial); + int id = (int)poseArray[baseIndex]; + double txnc = poseArray[baseIndex + 1]; + double tync = poseArray[baseIndex + 2]; + double ta = poseArray[baseIndex + 3]; + double distToCamera = poseArray[baseIndex + 4]; + double distToRobot = poseArray[baseIndex + 5]; + double ambiguity = poseArray[baseIndex + 6]; + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + } + + return new PoseEstimate(pose, adjustedTimestamp, latency, tagCount, tagSpan, tagDist, tagArea, rawFiducials, isMegaTag2); + } + + /** + * Gets the latest raw fiducial/AprilTag detection results from NetworkTables. + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawFiducial objects containing detection details + */ + public static RawFiducial[] getRawFiducials(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawfiducials"); + var rawFiducialArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 7; + if (rawFiducialArray.length % valsPerEntry != 0) { + return new RawFiducial[0]; + } + + int numFiducials = rawFiducialArray.length / valsPerEntry; + RawFiducial[] rawFiducials = new RawFiducial[numFiducials]; + + for (int i = 0; i < numFiducials; i++) { + int baseIndex = i * valsPerEntry; + int id = (int) extractArrayEntry(rawFiducialArray, baseIndex); + double txnc = extractArrayEntry(rawFiducialArray, baseIndex + 1); + double tync = extractArrayEntry(rawFiducialArray, baseIndex + 2); + double ta = extractArrayEntry(rawFiducialArray, baseIndex + 3); + double distToCamera = extractArrayEntry(rawFiducialArray, baseIndex + 4); + double distToRobot = extractArrayEntry(rawFiducialArray, baseIndex + 5); + double ambiguity = extractArrayEntry(rawFiducialArray, baseIndex + 6); + + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + + return rawFiducials; + } + + /** + * Gets the latest raw neural detector results from NetworkTables + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawDetection objects containing detection details + */ + public static RawDetection[] getRawDetections(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawdetections"); + var rawDetectionArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 12; + if (rawDetectionArray.length % valsPerEntry != 0) { + return new RawDetection[0]; + } + + int numDetections = rawDetectionArray.length / valsPerEntry; + RawDetection[] rawDetections = new RawDetection[numDetections]; + + for (int i = 0; i < numDetections; i++) { + int baseIndex = i * valsPerEntry; // Starting index for this detection's data + int classId = (int) extractArrayEntry(rawDetectionArray, baseIndex); + double txnc = extractArrayEntry(rawDetectionArray, baseIndex + 1); + double tync = extractArrayEntry(rawDetectionArray, baseIndex + 2); + double ta = extractArrayEntry(rawDetectionArray, baseIndex + 3); + double corner0_X = extractArrayEntry(rawDetectionArray, baseIndex + 4); + double corner0_Y = extractArrayEntry(rawDetectionArray, baseIndex + 5); + double corner1_X = extractArrayEntry(rawDetectionArray, baseIndex + 6); + double corner1_Y = extractArrayEntry(rawDetectionArray, baseIndex + 7); + double corner2_X = extractArrayEntry(rawDetectionArray, baseIndex + 8); + double corner2_Y = extractArrayEntry(rawDetectionArray, baseIndex + 9); + double corner3_X = extractArrayEntry(rawDetectionArray, baseIndex + 10); + double corner3_Y = extractArrayEntry(rawDetectionArray, baseIndex + 11); + + rawDetections[i] = new RawDetection(classId, txnc, tync, ta, corner0_X, corner0_Y, corner1_X, corner1_Y, corner2_X, corner2_Y, corner3_X, corner3_Y); + } + + return rawDetections; + } + + /** + * Prints detailed information about a PoseEstimate to standard output. + * Includes timestamp, latency, tag count, tag span, average tag distance, + * average tag area, and detailed information about each detected fiducial. + * + * @param pose The PoseEstimate object to print. If null, prints "No PoseEstimate available." + */ + public static void printPoseEstimate(PoseEstimate pose) { + if (pose == null) { + System.out.println("No PoseEstimate available."); + return; + } + + System.out.printf("Pose Estimate Information:%n"); + System.out.printf("Timestamp (Seconds): %.3f%n", pose.timestampSeconds); + System.out.printf("Latency: %.3f ms%n", pose.latency); + System.out.printf("Tag Count: %d%n", pose.tagCount); + System.out.printf("Tag Span: %.2f meters%n", pose.tagSpan); + System.out.printf("Average Tag Distance: %.2f meters%n", pose.avgTagDist); + System.out.printf("Average Tag Area: %.2f%% of image%n", pose.avgTagArea); + System.out.printf("Is MegaTag2: %b%n", pose.isMegaTag2); + System.out.println(); + + if (pose.rawFiducials == null || pose.rawFiducials.length == 0) { + System.out.println("No RawFiducials data available."); + return; + } + + System.out.println("Raw Fiducials Details:"); + for (int i = 0; i < pose.rawFiducials.length; i++) { + RawFiducial fiducial = pose.rawFiducials[i]; + System.out.printf(" Fiducial #%d:%n", i + 1); + System.out.printf(" ID: %d%n", fiducial.id); + System.out.printf(" TXNC: %.2f%n", fiducial.txnc); + System.out.printf(" TYNC: %.2f%n", fiducial.tync); + System.out.printf(" TA: %.2f%n", fiducial.ta); + System.out.printf(" Distance to Camera: %.2f meters%n", fiducial.distToCamera); + System.out.printf(" Distance to Robot: %.2f meters%n", fiducial.distToRobot); + System.out.printf(" Ambiguity: %.2f%n", fiducial.ambiguity); + System.out.println(); + } + } + + public static Boolean validPoseEstimate(PoseEstimate pose) { + return pose != null && pose.rawFiducials != null && pose.rawFiducials.length != 0; + } + + public static NetworkTable getLimelightNTTable(String tableName) { + return NetworkTableInstance.getDefault().getTable(sanitizeName(tableName)); + } + + public static void Flush() { + NetworkTableInstance.getDefault().flush(); + } + + public static NetworkTableEntry getLimelightNTTableEntry(String tableName, String entryName) { + return getLimelightNTTable(tableName).getEntry(entryName); + } + + public static DoubleArrayEntry getLimelightDoubleArrayEntry(String tableName, String entryName) { + String key = tableName + "/" + entryName; + return doubleArrayEntries.computeIfAbsent(key, k -> { + NetworkTable table = getLimelightNTTable(tableName); + return table.getDoubleArrayTopic(entryName).getEntry(new double[0]); + }); + } + + public static double getLimelightNTDouble(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDouble(0.0); + } + + public static void setLimelightNTDouble(String tableName, String entryName, double val) { + getLimelightNTTableEntry(tableName, entryName).setDouble(val); + } + + public static void setLimelightNTDoubleArray(String tableName, String entryName, double[] val) { + getLimelightNTTableEntry(tableName, entryName).setDoubleArray(val); + } + + public static double[] getLimelightNTDoubleArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDoubleArray(new double[0]); + } + + + public static String getLimelightNTString(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getString(""); + } + + public static String[] getLimelightNTStringArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getStringArray(new String[0]); + } + + + public static URL getLimelightURLString(String tableName, String request) { + String urlString = "http://" + sanitizeName(tableName) + ".local:5807/" + request; + URL url; + try { + url = new URL(urlString); + return url; + } catch (MalformedURLException e) { + System.err.println("bad LL URL"); + } + return null; + } + ///// + ///// + + /** + * Does the Limelight have a valid target? + * @param limelightName Name of the Limelight camera ("" for default) + * @return True if a valid target is present, false otherwise + */ + public static boolean getTV(String limelightName) { + return 1.0 == getLimelightNTDouble(limelightName, "tv"); + } + + /** + * Gets the horizontal offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTX(String limelightName) { + return getLimelightNTDouble(limelightName, "tx"); + } + + /** + * Gets the vertical offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTY(String limelightName) { + return getLimelightNTDouble(limelightName, "ty"); + } + + /** + * Gets the horizontal offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTXNC(String limelightName) { + return getLimelightNTDouble(limelightName, "txnc"); + } + + /** + * Gets the vertical offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTYNC(String limelightName) { + return getLimelightNTDouble(limelightName, "tync"); + } + + /** + * Gets the target area as a percentage of the image (0-100%). + * @param limelightName Name of the Limelight camera ("" for default) + * @return Target area percentage (0-100) + */ + public static double getTA(String limelightName) { + return getLimelightNTDouble(limelightName, "ta"); + } + + /** + * T2D is an array that contains several targeting metrcis + * @param limelightName Name of the Limelight camera + * @return Array containing [targetValid, targetCount, targetLatency, captureLatency, tx, ty, txnc, tync, ta, tid, targetClassIndexDetector, + * targetClassIndexClassifier, targetLongSidePixels, targetShortSidePixels, targetHorizontalExtentPixels, targetVerticalExtentPixels, targetSkewDegrees] + */ + public static double[] getT2DArray(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "t2d"); + } + + /** + * Gets the number of targets currently detected. + * @param limelightName Name of the Limelight camera + * @return Number of detected targets + */ + public static int getTargetCount(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[1]; + } + return 0; + } + + /** + * Gets the classifier class index from the currently running neural classifier pipeline + * @param limelightName Name of the Limelight camera + * @return Class index from classifier pipeline + */ + public static int getClassifierClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[10]; + } + return 0; + } + + /** + * Gets the detector class index from the primary result of the currently running neural detector pipeline. + * @param limelightName Name of the Limelight camera + * @return Class index from detector pipeline + */ + public static int getDetectorClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[11]; + } + return 0; + } + + /** + * Gets the current neural classifier result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from classifier pipeline + */ + public static String getClassifierClass (String limelightName) { + return getLimelightNTString(limelightName, "tcclass"); + } + + /** + * Gets the primary neural detector result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from detector pipeline + */ + public static String getDetectorClass (String limelightName) { + return getLimelightNTString(limelightName, "tdclass"); + } + + /** + * Gets the pipeline's processing latency contribution. + * @param limelightName Name of the Limelight camera + * @return Pipeline latency in milliseconds + */ + public static double getLatency_Pipeline(String limelightName) { + return getLimelightNTDouble(limelightName, "tl"); + } + + /** + * Gets the capture latency. + * @param limelightName Name of the Limelight camera + * @return Capture latency in milliseconds + */ + public static double getLatency_Capture(String limelightName) { + return getLimelightNTDouble(limelightName, "cl"); + } + + /** + * Gets the active pipeline index. + * @param limelightName Name of the Limelight camera + * @return Current pipeline index (0-9) + */ + public static double getCurrentPipelineIndex(String limelightName) { + return getLimelightNTDouble(limelightName, "getpipe"); + } + + /** + * Gets the current pipeline type. + * @param limelightName Name of the Limelight camera + * @return Pipeline type string (e.g. "retro", "apriltag", etc) + */ + public static String getCurrentPipelineType(String limelightName) { + return getLimelightNTString(limelightName, "getpipetype"); + } + + /** + * Gets the full JSON results dump. + * @param limelightName Name of the Limelight camera + * @return JSON string containing all current results + */ + public static String getJSONDump(String limelightName) { + return getLimelightNTString(limelightName, "json"); + } + + /** + * Switch to getBotPose + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + /** + * Switch to getBotPose_wpiRed + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + /** + * Switch to getBotPose_wpiBlue + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + public static double[] getBotPose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + public static double[] getBotPose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + } + + public static double[] getCameraPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + } + + public static double[] getTargetPose_CameraSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + } + + public static double[] getTargetPose_RobotSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + } + + public static double[] getTargetColor(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "tc"); + } + + public static double getFiducialID(String limelightName) { + return getLimelightNTDouble(limelightName, "tid"); + } + + public static String getNeuralClassID(String limelightName) { + return getLimelightNTString(limelightName, "tclass"); + } + + public static String[] getRawBarcodeData(String limelightName) { + return getLimelightNTStringArray(limelightName, "rawbarcodes"); + } + + ///// + ///// + + public static Pose3d getBotPose3d(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose"); + return toPose3D(poseArray); + } + + /** + * (Not Recommended) Gets the robot's 3D pose in the WPILib Red Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Red Alliance field space + */ + public static Pose3d getBotPose3d_wpiRed(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + return toPose3D(poseArray); + } + + /** + * (Recommended) Gets the robot's 3D pose in the WPILib Blue Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Blue Alliance field space + */ + public static Pose3d getBotPose3d_wpiBlue(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + return toPose3D(poseArray); + } + + /** + * Gets the robot's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation relative to the target + */ + public static Pose3d getBotPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the target + */ + public static Pose3d getCameraPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the camera's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the camera + */ + public static Pose3d getTargetPose3d_CameraSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the robot + */ + public static Pose3d getTargetPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the robot + */ + public static Pose3d getCameraPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiBlue(String limelightName) { + + double[] result = getBotPose_wpiBlue(limelightName); + return toPose2D(result); + } + + /** + * Gets the MegaTag1 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpiblue", false); + } + + /** + * Gets the MegaTag2 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * Make sure you are calling setRobotOrientation() before calling this method. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpiblue", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiRed(String limelightName) { + + double[] result = getBotPose_wpiRed(limelightName); + return toPose2D(result); + + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpired", false); + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpired", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d(String limelightName) { + + double[] result = getBotPose(limelightName); + return toPose2D(result); + + } + + /** + * Gets the current IMU data from NetworkTables. + * IMU data is formatted as [robotYaw, Roll, Pitch, Yaw, gyroX, gyroY, gyroZ, accelX, accelY, accelZ]. + * Returns all zeros if data is invalid or unavailable. + * + * @param limelightName Name/identifier of the Limelight + * @return IMUData object containing all current IMU data + */ + public static IMUData getIMUData(String limelightName) { + double[] imuData = getLimelightNTDoubleArray(limelightName, "imu"); + if (imuData == null || imuData.length < 10) { + return new IMUData(); // Returns object with all zeros + } + return new IMUData(imuData); + } + + ///// + ///// + + public static void setPipelineIndex(String limelightName, int pipelineIndex) { + setLimelightNTDouble(limelightName, "pipeline", pipelineIndex); + } + + + public static void setPriorityTagID(String limelightName, int ID) { + setLimelightNTDouble(limelightName, "priorityid", ID); + } + + /** + * Sets LED mode to be controlled by the current pipeline. + * @param limelightName Name of the Limelight camera + */ + public static void setLEDMode_PipelineControl(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 0); + } + + public static void setLEDMode_ForceOff(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 1); + } + + public static void setLEDMode_ForceBlink(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 2); + } + + public static void setLEDMode_ForceOn(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 3); + } + + /** + * Enables standard side-by-side stream mode. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_Standard(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 0); + } + + /** + * Enables Picture-in-Picture mode with secondary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPMain(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 1); + } + + /** + * Enables Picture-in-Picture mode with primary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPSecondary(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 2); + } + + + /** + * Sets the crop window for the camera. The crop window in the UI must be completely open. + * @param limelightName Name of the Limelight camera + * @param cropXMin Minimum X value (-1 to 1) + * @param cropXMax Maximum X value (-1 to 1) + * @param cropYMin Minimum Y value (-1 to 1) + * @param cropYMax Maximum Y value (-1 to 1) + */ + public static void setCropWindow(String limelightName, double cropXMin, double cropXMax, double cropYMin, double cropYMax) { + double[] entries = new double[4]; + entries[0] = cropXMin; + entries[1] = cropXMax; + entries[2] = cropYMin; + entries[3] = cropYMax; + setLimelightNTDoubleArray(limelightName, "crop", entries); + } + + /** + * Sets 3D offset point for easy 3D targeting. + */ + public static void setFiducial3DOffset(String limelightName, double offsetX, double offsetY, double offsetZ) { + double[] entries = new double[3]; + entries[0] = offsetX; + entries[1] = offsetY; + entries[2] = offsetZ; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Sets robot orientation values used by MegaTag2 localization algorithm. + * + * @param limelightName Name/identifier of the Limelight + * @param yaw Robot yaw in degrees. 0 = robot facing red alliance wall in FRC + * @param yawRate (Unnecessary) Angular velocity of robot yaw in degrees per second + * @param pitch (Unnecessary) Robot pitch in degrees + * @param pitchRate (Unnecessary) Angular velocity of robot pitch in degrees per second + * @param roll (Unnecessary) Robot roll in degrees + * @param rollRate (Unnecessary) Angular velocity of robot roll in degrees per second + */ + public static void SetRobotOrientation(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, true); + } + + public static void SetRobotOrientation_NoFlush(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, false); + } + + private static void SetRobotOrientation_INTERNAL(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate, boolean flush) { + + double[] entries = new double[6]; + entries[0] = yaw; + entries[1] = yawRate; + entries[2] = pitch; + entries[3] = pitchRate; + entries[4] = roll; + entries[5] = rollRate; + setLimelightNTDoubleArray(limelightName, "robot_orientation_set", entries); + if(flush) + { + Flush(); + } + } + + /** + * Configures the IMU mode for MegaTag2 Localization + * + * @param limelightName Name/identifier of the Limelight + * @param mode IMU mode. + */ + public static void SetIMUMode(String limelightName, int mode) { + setLimelightNTDouble(limelightName, "imumode_set", mode); + } + + /** + * Sets the 3D point-of-interest offset for the current fiducial pipeline. + * https://docs.limelightvision.io/docs/docs-limelight/pipeline-apriltag/apriltag-3d#point-of-interest-tracking + * + * @param limelightName Name/identifier of the Limelight + * @param x X offset in meters + * @param y Y offset in meters + * @param z Z offset in meters + */ + public static void SetFidcuial3DOffset(String limelightName, double x, double y, + double z) { + + double[] entries = new double[3]; + entries[0] = x; + entries[1] = y; + entries[2] = z; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Overrides the valid AprilTag IDs that will be used for localization. + * Tags not in this list will be ignored for robot pose estimation. + * + * @param limelightName Name/identifier of the Limelight + * @param validIDs Array of valid AprilTag IDs to track + */ + public static void SetFiducialIDFiltersOverride(String limelightName, int[] validIDs) { + double[] validIDsDouble = new double[validIDs.length]; + for (int i = 0; i < validIDs.length; i++) { + validIDsDouble[i] = validIDs[i]; + } + setLimelightNTDoubleArray(limelightName, "fiducial_id_filters_set", validIDsDouble); + } + + /** + * Sets the downscaling factor for AprilTag detection. + * Increasing downscale can improve performance at the cost of potentially reduced detection range. + * + * @param limelightName Name/identifier of the Limelight + * @param downscale Downscale factor. Valid values: 1.0 (no downscale), 1.5, 2.0, 3.0, 4.0. Set to 0 for pipeline control. + */ + public static void SetFiducialDownscalingOverride(String limelightName, float downscale) + { + int d = 0; // pipeline + if (downscale == 1.0) + { + d = 1; + } + if (downscale == 1.5) + { + d = 2; + } + if (downscale == 2) + { + d = 3; + } + if (downscale == 3) + { + d = 4; + } + if (downscale == 4) + { + d = 5; + } + setLimelightNTDouble(limelightName, "fiducial_downscale_set", d); + } + + /** + * Sets the camera pose relative to the robot. + * @param limelightName Name of the Limelight camera + * @param forward Forward offset in meters + * @param side Side offset in meters + * @param up Up offset in meters + * @param roll Roll angle in degrees + * @param pitch Pitch angle in degrees + * @param yaw Yaw angle in degrees + */ + public static void setCameraPose_RobotSpace(String limelightName, double forward, double side, double up, double roll, double pitch, double yaw) { + double[] entries = new double[6]; + entries[0] = forward; + entries[1] = side; + entries[2] = up; + entries[3] = roll; + entries[4] = pitch; + entries[5] = yaw; + setLimelightNTDoubleArray(limelightName, "camerapose_robotspace_set", entries); + } + + ///// + ///// + + public static void setPythonScriptData(String limelightName, double[] outgoingPythonData) { + setLimelightNTDoubleArray(limelightName, "llrobot", outgoingPythonData); + } + + public static double[] getPythonScriptData(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "llpython"); + } + + ///// + ///// + + /** + * Asynchronously take snapshot. + */ + public static CompletableFuture takeSnapshot(String tableName, String snapshotName) { + return CompletableFuture.supplyAsync(() -> { + return SYNCH_TAKESNAPSHOT(tableName, snapshotName); + }); + } + + private static boolean SYNCH_TAKESNAPSHOT(String tableName, String snapshotName) { + URL url = getLimelightURLString(tableName, "capturesnapshot"); + try { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + if (snapshotName != null && snapshotName != "") { + connection.setRequestProperty("snapname", snapshotName); + } + + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + return true; + } else { + System.err.println("Bad LL Request"); + } + } catch (IOException e) { + System.err.println(e.getMessage()); + } + return false; + } + + /** + * Gets the latest JSON results output and returns a LimelightResults object. + * @param limelightName Name of the Limelight camera + * @return LimelightResults object containing all current target data + */ + public static LimelightResults getLatestResults(String limelightName) { + + long start = System.nanoTime(); + LimelightHelpers.LimelightResults results = new LimelightHelpers.LimelightResults(); + if (mapper == null) { + mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + try { + results = mapper.readValue(getJSONDump(limelightName), LimelightResults.class); + } catch (JsonProcessingException e) { + results.error = "lljson error: " + e.getMessage(); + } + + long end = System.nanoTime(); + double millis = (end - start) * .000001; + results.latency_jsonParse = millis; + if (profileJSON) { + System.out.printf("lljson: %.2f\r\n", millis); + } + + return results; + } +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Main.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Robot.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..687a0a0 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/Robot.java @@ -0,0 +1,103 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.TimedRobot; +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 functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private Command m_autonomousCommand; + + private RobotContainer m_robotContainer; + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + @Override + public void robotInit() { + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); + } + + /** + * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics + * that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @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(); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); + + // schedule the autonomous command (example) + if (m_autonomousCommand != null) { + m_autonomousCommand.schedule(); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} + + /** This function is called once when the robot is first started up. */ + @Override + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/RobotContainer.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..7f035cd --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,156 @@ +// 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 frc.robot; + +import java.util.ArrayList; +import java.util.Optional; + +import com.ctre.phoenix6.hardware.TalonFX; +import com.pathplanner.lib.auto.AutoBuilder; +import com.studica.frc.AHRS; +import com.studica.frc.AHRS.NavXComType; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.factorys.DriveSubsystemFactory; +import frc.robot.factorys.TalonFXFactory; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.utils.AutoCommands; + +/** + * 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 trigger mappings) should be declared here. + */ +public class RobotContainer { + // The robot's subsystems and commands are defined here... + private final Optional m_driveSubsystem; + + // Replace with CommandPS4Controller or CommandJoystick if needed + //private final CommandXboxController m_driverController = + // new CommandXboxController(OperatorConstants.kDriverControllerPort); + + private final AHRS m_gyro = new AHRS(NavXComType.kMXP_SPI); + private final DifferentialDriveKinematics m_DriveKinematics = new DifferentialDriveKinematics(DrivetrainConstants.trackWidthMeters); + + // Pose Estimators + private DifferentialDrivePoseEstimator m_DrivePoseEstimator = new DifferentialDrivePoseEstimator( + m_DriveKinematics, + m_gyro.getRotation2d(), + 0, + 0, + new Pose2d()); + + // Sendable Choosers + private SendableChooser autoChooser; + + // Factorys + private TalonFXFactory m_TalonFXFactory = new TalonFXFactory(); + private DriveSubsystemFactory m_DriveSubsystemFactory = new DriveSubsystemFactory(); + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + // Init DriveSubsystem + Optional rightLead = m_TalonFXFactory.construct(DrivetrainConstants.kRightMotorCANID); + Optional leftLead = m_TalonFXFactory.construct(DrivetrainConstants.kLeftMotorCANID); + Optional rightFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalRightMotorCANID); + Optional leftFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalLeftMotorCANID); + m_driveSubsystem = m_DriveSubsystemFactory.construct(m_DrivePoseEstimator, m_DriveKinematics, m_gyro, rightLead, leftLead, rightFollower, leftFollower); + + // Init the subsystems + //m_limelightSubsystem = getSubsystem(LimelightSubsystem.class, m_limeLightPoseEstimator); + //m_exampleSubsystem = getSubsystem(ExampleSubsystem.class); + + // Init Auto + configureAutos(); + + // Configure the default commands + configureDefaultCommands(); + + // Configure the trigger bindings + configureBindings(); + } + + // Tom wrote this cool template to make the optional subsystem creation code in + // the constructor above a lot clearer. This is what clever coding looks like. + // Owen: I added the ability to pass object args through this for dependency injection, + // I hope this does not break anything or commited a coding sin or somthing + // private static Optional getSubsystem(Class subsystemClass, Object... args) { + // Optional iss; + // try { + + // iss = Optional.ofNullable(subsystemClass.getDeclaredConstructor().newInstance(args)); + // } catch (Exception e) { + // iss = Optional.empty(); + // // This is not tested! - Owen + // DriverStation.reportWarning( + // String.format( + // "The %s was not found!", subsystemClass.getName()), + // false + // ); + // } + // return iss; + // } + + private void configureAutos() { + // Init Autos (/home/lvuser/deploy/pathplanner/autos) + ArrayList autoCommands = AutoCommands.getAutoCommands(m_driveSubsystem); + + // Init Chooser + autoChooser = AutoBuilder.buildAutoChooser(); // Can make a default by giving a string + SmartDashboard.putData("Auto Chooser", autoChooser); + } + + private void configureDefaultCommands() { + if (m_driveSubsystem.isPresent()) + { + //DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + //driveSubsystem.initDefaultCommand(m_driverController); + } + } + + /** + * Use this method to define your trigger->command mappings. Triggers can be created via the + * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary + * predicate, or via the named factories in {@link + * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link + * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller + * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight + * joysticks}. + */ + private void configureBindings() { + // SmartDashboard.putBoolean("Example Subsystem", m_exampleSubsystem.isPresent()); + + // if (m_exampleSubsystem.isPresent()) + // { + // ExampleSubsystem exampleSubsystem = m_exampleSubsystem.get(); + + // // Schedule `ExampleCommand` when `exampleCondition` changes to `true` + // new Trigger(exampleSubsystem::exampleCondition) + // .onTrue(new ExampleCommand(exampleSubsystem)); + + // // Schedule `exampleMethodCommand` when the Xbox controller's B button is pressed, + // // cancelling on release. + // m_driverController.b().whileTrue(exampleSubsystem.exampleMethodCommand()); + // } + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + return autoChooser.getSelected(); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ArcadeDriveCommand.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ArcadeDriveCommand.java new file mode 100644 index 0000000..a641317 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ArcadeDriveCommand.java @@ -0,0 +1,53 @@ +// 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 frc.robot.commands; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class ArcadeDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + m_subsystem.setDifferentialSpeeds( + -m_driveController.getRightY(), + -m_driveController.getLeftX() + ); + } + + // 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/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/Autos.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/Autos.java @@ -0,0 +1,20 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; + +public final class Autos { + /** Example static factory for an autonomous command. */ + public static Command exampleAuto(ExampleSubsystem subsystem) { + return Commands.sequence(subsystem.exampleMethodCommand(), new ExampleCommand(subsystem)); + } + + private Autos() { + throw new UnsupportedOperationException("This is a utility class!"); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/DifferentialDriveCommand.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/DifferentialDriveCommand.java new file mode 100644 index 0000000..27f220a --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/DifferentialDriveCommand.java @@ -0,0 +1,57 @@ +// 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 frc.robot.commands; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants; + +/** An example command that uses an example subsystem. */ +public class DifferentialDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public DifferentialDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double leftSpeed = -m_driveController.getLeftY(); + double rightSpeed = -m_driveController.getRightY(); + + leftSpeed *= Constants.DrivetrainConstants.maxVelocityMPS; + rightSpeed *= Constants.DrivetrainConstants.maxVelocityMPS; + + m_subsystem.setDifferentialSpeeds( leftSpeed, rightSpeed ); + } + + // 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/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ExampleCommand.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..151624c --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/commands/ExampleCommand.java @@ -0,0 +1,43 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; + +/** An example command that uses an example subsystem. */ +public class ExampleCommand extends Command { + @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField", "unused"}) + private final ExampleSubsystem m_subsystem; + + /** + * Creates a new ExampleCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExampleCommand(ExampleSubsystem subsystem) { + m_subsystem = subsystem; + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(subsystem); + } + + // 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() {} + + // 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/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java new file mode 100644 index 0000000..afc5b84 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java @@ -0,0 +1,33 @@ +package frc.robot.factorys; + +import java.util.Optional; +import com.ctre.phoenix6.hardware.TalonFX; +import com.studica.frc.AHRS; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import frc.robot.subsystems.DriveSubsystem; + +public class DriveSubsystemFactory { + public DriveSubsystemFactory() { + } + + public Optional construct(DifferentialDrivePoseEstimator poseEstimator, + DifferentialDriveKinematics kinematics, + AHRS gyro, Optional rightLead, Optional leftLead, Optional rightFollower, + Optional leftFollower) { + + if (rightLead.isEmpty() || leftLead.isEmpty()) { + return Optional.empty(); + } + + DriveSubsystem driveSubsystem = new DriveSubsystem(poseEstimator, kinematics, gyro, rightLead.get(), + leftLead.get()); + + if (rightFollower.isPresent() && leftFollower.isPresent()) { + driveSubsystem.setFollowers(rightFollower.get(), leftFollower.get()); + } + + return Optional.of(driveSubsystem); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/TalonFXFactory.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/TalonFXFactory.java new file mode 100644 index 0000000..1a84899 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/factorys/TalonFXFactory.java @@ -0,0 +1,20 @@ +package frc.robot.factorys; + +import java.util.Optional; + +import com.ctre.phoenix6.hardware.TalonFX; + +public class TalonFXFactory { + public TalonFXFactory() {} + + public Optional construct(int CANID) { + Optional newMotor; + try { + newMotor = Optional.of(new TalonFX(CANID)); + } catch (Exception e) { + newMotor = Optional.empty(); + } + + return newMotor; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/DriveSubsystem.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/DriveSubsystem.java new file mode 100644 index 0000000..6782406 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/DriveSubsystem.java @@ -0,0 +1,285 @@ +// 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. + +// https://github.com/CrossTheRoadElec/Phoenix6-Examples/blob/main/java/VelocityClosedLoop/src/main/java/frc/robot/Robot.java +// This is a link to do speed control on the kraken motors. We need something like this. +package frc.robot.subsystems; +import static edu.wpi.first.units.Units.Volts; + +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.controls.VelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.controllers.PPLTVController; +import com.studica.frc.AHRS; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.drive.DifferentialDrive; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants.AutoConstants; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.commands.DifferentialDriveCommand; +//import frc.robot.utils.PIDControl; + +public class DriveSubsystem extends SubsystemBase { + private TalonFX m_rightMotor; + private TalonFX m_optionalRightMotor; + + private TalonFX m_leftMotor; + private TalonFX m_optionalLeftMotor; + + /* Start at velocity 0, use slot 0 */ + private final VelocityVoltage m_leftVelocityVoltage = new VelocityVoltage(0).withSlot(0); + private final VelocityVoltage m_rightVelocityVoltage = new VelocityVoltage(0).withSlot(0); + + private DifferentialDrive m_Drivetrain; + + private double m_leftSpeed = 0.0; + private double m_rightSpeed = 0.0; + + private final DifferentialDriveKinematics m_kinematics; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final AHRS m_gyro; + + // PIDControl m_leftPidControl = new PIDControl( + // m_leftMotor, + // DrivetrainConstants.kLeftPositiveMovesForward + // ); + + // PIDControl m_rightPidControl = new PIDControl( + // m_rightMotor, + // DrivetrainConstants.kRightPositiveMovesForward + // ); + + /** Creates a new DriveSubsystem. */ + public DriveSubsystem(DifferentialDrivePoseEstimator poseEstimator, DifferentialDriveKinematics kinematics, AHRS gyro, TalonFX rightMotor, TalonFX leftMotor) { + m_poseEstimator = poseEstimator; + m_kinematics = kinematics; + m_gyro = gyro; + m_rightMotor = rightMotor; + m_leftMotor = leftMotor; + + // Right Motor + TalonFXConfiguration rightMotorConfig = new TalonFXConfiguration(); + rightMotorConfig.Slot0.kV = DrivetrainConstants.kV; + rightMotorConfig.Slot0.kS = DrivetrainConstants.kS; + rightMotorConfig.Slot0.kP = DrivetrainConstants.kP; + rightMotorConfig.Slot0.kI = DrivetrainConstants.kI; // No output for integrated error + rightMotorConfig.Slot0.kD = DrivetrainConstants.kD; // A velocity of 1 rps results in 0.1 V output + rightMotorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + rightMotorConfig.MotorOutput.Inverted = InvertedValue.Clockwise_Positive; + + rightMotorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.PeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.PeakVoltage)); + + m_rightMotor.getConfigurator().apply(rightMotorConfig); + SendableRegistry.setName(m_rightMotor, "DriveSubsystem", "rightMotor"); + + // Left Motor + TalonFXConfiguration leftMotorConfig = new TalonFXConfiguration(); + leftMotorConfig.Slot0.kV = DrivetrainConstants.kV; + leftMotorConfig.Slot0.kS = DrivetrainConstants.kS; + leftMotorConfig.Slot0.kP = DrivetrainConstants.kP; + leftMotorConfig.Slot0.kI = DrivetrainConstants.kI; // No output for integrated error + leftMotorConfig.Slot0.kD = DrivetrainConstants.kD; // A velocity of 1 rps results in 0.1 V output + leftMotorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + leftMotorConfig.MotorOutput.Inverted = InvertedValue.CounterClockwise_Positive; + + leftMotorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.PeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.PeakVoltage)); + + m_leftMotor.getConfigurator().apply(leftMotorConfig); + SendableRegistry.setName(m_leftMotor, "DriveSubsystem", "leftMotor"); + + // Zeroing the encoders + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + + // Setting up the drive train + m_Drivetrain = new DifferentialDrive(m_leftMotor::set, m_rightMotor::set); + SendableRegistry.setName(m_Drivetrain, "DriveSubsystem", "Drivetrain"); + + // Init Autobuilder + AutoBuilder.configure( + this::getPose, + this::resetPose, + this::getRobotRelativeSpeeds, + (speeds, feedforwards) -> driveRobotRelative(speeds), + new PPLTVController(0.02), + AutoConstants.kRobotConfig, + () -> { + // Boolean supplier that controls when the path will be mirrored for the red alliance + // This will flip the path being followed to the red side of the field. + // THE ORIGIN WILL REMAIN ON THE BLUE SIDE + + var alliance = DriverStation.getAlliance(); + if (alliance.isPresent()) { + return alliance.get() == DriverStation.Alliance.Red; + } + return false; + }, + this + ); + SendableRegistry.setName(m_Drivetrain, "DriveSubsystem", "Drivetrain"); + + // Gyro setup + m_gyro.zeroYaw(); + } + + public void setFollowers(TalonFX optionalRight, TalonFX optionalLeft) { + m_optionalRightMotor = optionalRight; + + // Setting up Config + TalonFXConfiguration optionalRightMotorConfig = new TalonFXConfiguration(); + //check + optionalRightMotorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + optionalRightMotorConfig.MotorOutput.Inverted = InvertedValue.Clockwise_Positive; + + optionalRightMotorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.PeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.PeakVoltage)); + + // Saving + m_optionalRightMotor.getConfigurator().apply(optionalRightMotorConfig); + + // Setting as a follwer + m_optionalRightMotor.setControl( + new Follower(m_rightMotor.getDeviceID(), false) + ); + + m_optionalLeftMotor = optionalLeft; + + // Setting up Config + TalonFXConfiguration optionalLeftMotorConfig = new TalonFXConfiguration(); + optionalLeftMotorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + optionalLeftMotorConfig.MotorOutput.Inverted = InvertedValue.CounterClockwise_Positive; + + optionalLeftMotorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.PeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.PeakVoltage)); + + // Saving + m_optionalLeftMotor.getConfigurator().apply(optionalLeftMotorConfig); + + // Setting as follower + m_optionalLeftMotor.setControl( + new Follower(m_leftMotor.getDeviceID(), false) + ); + } + + public void initDefaultCommand(CommandXboxController Controller) + { + setDefaultCommand(new DifferentialDriveCommand(this, Controller)); + } + + public void setDifferentialSpeeds(double leftSpeedMPS, double rightSpeedMPS) + { + // NOTE: We are making our own custom input modifications + //m_leftSpeed = Math.pow(m_leftSpeed, 3); + //m_rightSpeed = Math.pow(m_rightSpeed, 3); + // NOTE: Notes can be useful for conveying information + + SmartDashboard.putNumber("Left Set", leftSpeedMPS); + SmartDashboard.putNumber("Right Set", rightSpeedMPS); + + double leftMPS = getMotorSpeedMPS(true); + double rightMPS = getMotorSpeedMPS(false); + + SmartDashboard.putNumber("Left MPS", leftMPS); + SmartDashboard.putNumber("Right MPS", rightMPS); + + // set motor voltage based on CTRE example + + double desiredLeftRotationsPerSecond =(leftMPS/Constants.DrivetrainConstants.kWheelCircumference)/Constants.DrivetrainConstants.kDrivetrainGearRatio; + double desiredRightRotationsPerSecond =(rightMPS/Constants.DrivetrainConstants.kWheelCircumference)/Constants.DrivetrainConstants.kDrivetrainGearRatio; + + m_leftMotor.setControl(m_leftVelocityVoltage.withVelocity(desiredLeftRotationsPerSecond)); + m_rightMotor.setControl(m_rightVelocityVoltage.withVelocity(desiredRightRotationsPerSecond)); + + } + + public double getSpeed(boolean bLeft) + { + if (bLeft) + { + return m_leftSpeed; + } + else + { + return m_rightSpeed; + } + } + + // Wrapping robot position inside of getposition + public Pose2d getPose(){ + return m_poseEstimator.getEstimatedPosition(); + } + + // Resets the drivetrains pose estimator to zero. + public void resetPose(Pose2d newPose){ + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + m_poseEstimator.resetPose(newPose); + } + + public double getMotorSpeedMPS(boolean bLeft) + { + double MPS; + if (bLeft) + { + MPS = m_leftMotor.getVelocity().getValueAsDouble() * DrivetrainConstants.kDrivetrainGearRatio * DrivetrainConstants.kWheelCircumference; + } + else + { + MPS = m_rightMotor.getVelocity().getValueAsDouble() * DrivetrainConstants.kDrivetrainGearRatio * DrivetrainConstants.kWheelCircumference; + } + return MPS; + } + + + // Returns a robot relative ChassisSpeeds object based on the avrg linear velocity + // in meters per second and avrg anglear velocity in readians per second + // Currently we are asuming that their is no scale for the motors, we cannot find anywhere to set the scale. + public ChassisSpeeds getRobotRelativeSpeeds() + { + // Linear Velocity in meters per second + double leftMPS = getMotorSpeedMPS(true); + double rightMPS = getMotorSpeedMPS(false); + + // Make wheelSpeeds object from MPS & converts it to chasis speeds + DifferentialDriveWheelSpeeds wheelSpeeds = new DifferentialDriveWheelSpeeds(leftMPS, rightMPS); + return m_kinematics.toChassisSpeeds(wheelSpeeds); + } + + // Gives the drivetrain a new drive command based on a robot relative + // chassis speed object. + public void driveRobotRelative(ChassisSpeeds relativeChassisSpeed){ + m_Drivetrain.arcadeDrive(relativeChassisSpeed.vxMetersPerSecond, + relativeChassisSpeed.omegaRadiansPerSecond); + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + + m_poseEstimator.update( + m_gyro.getRotation2d(), + m_leftMotor.getPosition().getValueAsDouble(), + m_rightMotor.getPosition().getValueAsDouble()); + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..6b375da --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/ExampleSubsystem.java @@ -0,0 +1,47 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class ExampleSubsystem extends SubsystemBase { + /** Creates a new ExampleSubsystem. */ + public ExampleSubsystem() {} + + /** + * Example command factory method. + * + * @return a command + */ + public Command exampleMethodCommand() { + // Inline construction of command goes here. + // Subsystem::RunOnce implicitly requires `this` subsystem. + return runOnce( + () -> { + /* one-time action goes here */ + }); + } + + /** + * An example method querying a boolean state of the subsystem (for example, a digital sensor). + * + * @return value of some boolean subsystem state, such as a digital sensor. + */ + public boolean exampleCondition() { + // Query some boolean state, such as a digital sensor. + return false; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/LimelightSubsystem.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/LimelightSubsystem.java new file mode 100644 index 0000000..0008e82 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/subsystems/LimelightSubsystem.java @@ -0,0 +1,48 @@ +// 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 frc.robot.subsystems; +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.LimelightHelpers; + +public class LimelightSubsystem extends SubsystemBase { + private final DifferentialDrivePoseEstimator m_poseEstimator; + + /** Creates a new PositionSubsystem. */ + public LimelightSubsystem(DifferentialDrivePoseEstimator poseEstimator) { + m_poseEstimator = poseEstimator; + } + + /** + * An example method querying a boolean state of the subsystem (for example, a digital sensor). + * + * @return value of some boolean subsystem state, such as a digital sensor. + */ + public boolean exampleCondition() { + // Query some boolean state, such as a digital sensor. + return false; + } + + @Override + public void periodic() { + // This method will be called once per scheduler + + // This is copy and pasted from limelight's documentation for testing. + LimelightHelpers.PoseEstimate limelightMeasurement = LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight"); + if (limelightMeasurement.tagCount >= 2) { // Only trust measurement if we see multiple tags + m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(0.7, 0.7, 9999999)); + m_poseEstimator.addVisionMeasurement( + limelightMeasurement.pose, + limelightMeasurement.timestampSeconds + ); + } + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/AutoCommands.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/AutoCommands.java new file mode 100644 index 0000000..5475bf5 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/AutoCommands.java @@ -0,0 +1,59 @@ +package frc.robot.utils; + +import java.io.File; +import edu.wpi.first.wpilibj.Filesystem; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +import java.util.ArrayList; +import java.util.Optional; + +import com.pathplanner.lib.commands.PathPlannerAuto; + +// This goes to "/home/lvuser/deploy/pathplanner/autos" and grabs the names of all .auto files +// We are asuming that the PathPlannerAuto(string) is the file name of the auto +// We are asuming that the files are always in the "/home/lvuser/deploy/pathplanner/autos" +// We found this information at https://github.com/mjansen4857/pathplanner/wiki/PathPlannerLib:-Java-Usage +public class AutoCommands { + public static ArrayList getAutoNames() { + // Creating an ArrayList so I can fill it with auto file names w/o the extention. + ArrayList autoNames = new ArrayList(); + + // Getting the folder and files. + String deployDirectoryPath = Filesystem.getDeployDirectory().getPath(); + File deployDirectory = new File(deployDirectoryPath+"/pathplanner/autos"); + File[] listOfFiles = deployDirectory.listFiles(); + + // Checking if the files exist + if (listOfFiles != null) { + for (File file : listOfFiles) { + String fullName = file.getName(); // Returns [file_name].[extention] + if (file.isFile()) { + String[] splitName = fullName.split("\\."); // Splitting the file name into seprate parts. + + // If its an auto file then add it to the list. + if (splitName.length > 1 && "auto".equals(splitName[1])) { + autoNames.add(splitName[0]); + System.out.println("FOUND AUTO: "+splitName[0]); + } + } + } + } + + // Returning the new array + return autoNames; + } + + public static ArrayList getAutoCommands(Optional driveSubsystem) { + ArrayList autoCommands = new ArrayList(); + + if (driveSubsystem.isPresent()) { + ArrayList autoNames = getAutoNames(); + for (String autoName : autoNames) { + autoCommands.add(new PathPlannerAuto(autoName)); + } + } + + return autoCommands; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/PIDControl.java b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/PIDControl.java new file mode 100644 index 0000000..ead0d7b --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/src/main/java/frc/robot/utils/PIDControl.java @@ -0,0 +1,69 @@ +package frc.robot.utils; + +import com.ctre.phoenix6.controls.PositionVoltage; +import com.ctre.phoenix6.hardware.TalonFX; + +import edu.wpi.first.math.trajectory.TrapezoidProfile; +import frc.robot.Constants.DrivetrainConstants; + +public class PIDControl { + private TalonFX m_motor; + private boolean m_positiveMovesForward; + + private double m_targetPosition; + + private final TrapezoidProfile m_profile = new TrapezoidProfile( + new TrapezoidProfile.Constraints( + DrivetrainConstants.maxVelocity, + DrivetrainConstants.maxAcceleration + ) + ); + + private PositionVoltage m_positionVoltage; + private TrapezoidProfile.State m_goal = new TrapezoidProfile.State(); + private TrapezoidProfile.State m_setPoint = new TrapezoidProfile.State(); + + public PIDControl(TalonFX motor, boolean positiveMovesForward) { + m_motor = motor; + m_positiveMovesForward = positiveMovesForward; + } + + public void setTargetPosition(double position) { + m_targetPosition = position; + + double positionRotations = position * DrivetrainConstants.kDrivetrainGearRatio; + if (!m_positiveMovesForward) { + positionRotations = -positionRotations; + } + + m_goal = new TrapezoidProfile.State(positionRotations, 0); + m_setPoint = new TrapezoidProfile.State(0, 0); + + m_positionVoltage = new PositionVoltage(0).withSlot(0); + + m_motor.setPosition(0); // zero's motor at the start of the movement + } + + public void calculatePosition() { + m_setPoint = m_profile.calculate(0.020, m_setPoint, m_goal); + + m_positionVoltage.Position = m_setPoint.position; + m_positionVoltage.Velocity = m_setPoint.velocity; + m_motor.setControl(m_positionVoltage); + } + + public double getTargetPosition() { + return m_targetPosition; + } + + public double getPosition() { + double motorPosition = m_motor.getPosition().getValueAsDouble() / DrivetrainConstants.kDrivetrainGearRatio; + + if (m_positiveMovesForward) { + return motorPosition; + } else { + return -motorPosition; + } + } + +} diff --git a/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/Screenshot (1).png b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/Screenshot (1).png new file mode 100644 index 0000000..1130979 Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/Screenshot (1).png differ diff --git a/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/hoots/rio_2025-11-11_19-44-12.hoot b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/hoots/rio_2025-11-11_19-44-12.hoot new file mode 100644 index 0000000..2730420 Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/hoots/rio_2025-11-11_19-44-12.hoot differ diff --git a/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/wpilogs/rio_2025-11-11_19-44-12.hoot.wpilog b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/wpilogs/rio_2025-11-11_19-44-12.hoot.wpilog new file mode 100644 index 0000000..924414a Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2025/sysid_logs/wpilogs/rio_2025-11-11_19-44-12.hoot.wpilog differ diff --git a/prototype_projects/Experimental_Drivetrain_2025/vendordeps/PathplannerLib-2025.2.7.json b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/PathplannerLib-2025.2.7.json new file mode 100644 index 0000000..d3f84e5 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/PathplannerLib-2025.2.7.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2025.2.7.json", + "name": "PathplannerLib", + "version": "2025.2.7", + "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", + "frcYear": "2025", + "mavenUrls": [ + "https://3015rangerrobotics.github.io/pathplannerlib/repo" + ], + "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", + "javaDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-java", + "version": "2025.2.7" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2025.2.7", + "libName": "PathplannerLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Phoenix6-frc2025-latest.json b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Phoenix6-frc2025-latest.json new file mode 100644 index 0000000..6f40c84 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Phoenix6-frc2025-latest.json @@ -0,0 +1,479 @@ +{ + "fileName": "Phoenix6-frc2025-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "25.4.0", + "frcYear": "2025", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2025-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "25.4.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.4.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "25.4.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.4.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.4.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.4.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.4.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.4.0", + "libName": "CTRE_SimCANCoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.4.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.4.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.4.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.4.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "25.4.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "25.4.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/vendordeps/REVLib.json b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/REVLib.json new file mode 100644 index 0000000..ac62be8 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/REVLib.json @@ -0,0 +1,71 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2025.0.3", + "frcYear": "2025", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2025.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2025.0.3" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2025.0.3", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2025.0.3", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Studica-2025.0.1.json b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Studica-2025.0.1.json new file mode 100644 index 0000000..5010be0 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/Studica-2025.0.1.json @@ -0,0 +1,71 @@ +{ + "fileName": "Studica-2025.0.1.json", + "name": "Studica", + "version": "2025.0.1", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "frcYear": "2025", + "mavenUrls": [ + "https://dev.studica.com/maven/release/2025/" + ], + "jsonUrl": "https://dev.studica.com/releases/2025/Studica-2025.0.1.json", + "cppDependencies": [ + { + "artifactId": "Studica-cpp", + "binaryPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "linuxx86-64", + "osxuniversal", + "windowsx86-64" + ], + "groupId": "com.studica.frc", + "headerClassifier": "headers", + "libName": "Studica", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "version": "2025.0.1" + }, + { + "artifactId": "Studica-driver", + "binaryPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "linuxx86-64", + "osxuniversal", + "windowsx86-64" + ], + "groupId": "com.studica.frc", + "headerClassifier": "headers", + "libName": "StudicaDriver", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "version": "2025.0.1" + } + ], + "javaDependencies": [ + { + "artifactId": "Studica-java", + "groupId": "com.studica.frc", + "version": "2025.0.1" + } + ], + "jniDependencies": [ + { + "artifactId": "Studica-driver", + "groupId": "com.studica.frc", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "linuxx86-64", + "osxuniversal", + "windowsx86-64" + ], + "version": "2025.0.1" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2025/vendordeps/WPILibNewCommands.json b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..3718e0a --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2025/vendordeps/WPILibNewCommands.json @@ -0,0 +1,38 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2025", + "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" + ] + } + ] +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/.gitignore b/prototype_projects/Experimental_Drivetrain_2026/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/Experimental_Drivetrain_2026/.vscode/launch.json b/prototype_projects/Experimental_Drivetrain_2026/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/.vscode/settings.json b/prototype_projects/Experimental_Drivetrain_2026/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/.wpilib/wpilib_preferences.json b/prototype_projects/Experimental_Drivetrain_2026/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..994eeed --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/AutoStatistics.txt b/prototype_projects/Experimental_Drivetrain_2026/AutoStatistics.txt new file mode 100644 index 0000000..5d40e72 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/AutoStatistics.txt @@ -0,0 +1,14 @@ +Test 1: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch + +Test 2: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch + +Test 3: +Auto: SmoothTurnReverseS +Rotation Off: 0 degrees (essentially) +Pos Off: 8 inch \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/DriveForwardFromPosMath.png b/prototype_projects/Experimental_Drivetrain_2026/DriveForwardFromPosMath.png new file mode 100644 index 0000000..82be272 Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2026/DriveForwardFromPosMath.png differ diff --git a/prototype_projects/Experimental_Drivetrain_2026/WPILib-License.md b/prototype_projects/Experimental_Drivetrain_2026/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/Experimental_Drivetrain_2026/build.gradle b/prototype_projects/Experimental_Drivetrain_2026/build.gradle new file mode 100644 index 0000000..a3a6e39 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.2.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = true // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/Experimental_Drivetrain_2026/elastic-layout.json b/prototype_projects/Experimental_Drivetrain_2026/elastic-layout.json new file mode 100644 index 0000000..9175fbd --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/elastic-layout.json @@ -0,0 +1,199 @@ +{ + "version": 1.0, + "grid_size": 128, + "tabs": [ + { + "name": "Toaster Testing", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Drive Commands", + "x": 0.0, + "y": 0.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Drive Commands", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Left Target (MPS)", + "x": 0.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Left Speed (MPS)", + "x": 0.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Right Target (MPS)", + "x": 128.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Target (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Right Speed (MPS)", + "x": 128.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Speed (MPS)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Auto Chooser", + "x": 0.0, + "y": 128.0, + "width": 256.0, + "height": 128.0, + "type": "ComboBox Chooser", + "properties": { + "topic": "/SmartDashboard/Auto Chooser", + "period": 0.06, + "sort_options": false + } + }, + { + "title": "Field", + "x": 384.0, + "y": 0.0, + "width": 640.0, + "height": 384.0, + "type": "Field", + "properties": { + "topic": "/SmartDashboard/Field", + "period": 0.06, + "field_game": "Rebuilt", + "robot_width": 0.85, + "robot_length": 0.85, + "show_other_objects": true, + "show_trajectories": true, + "field_rotation": 0.0, + "robot_color": 4294198070, + "trajectory_color": 4294967295, + "show_robot_outside_widget": true + } + }, + { + "title": "Scheduler", + "x": 1664.0, + "y": 0.0, + "width": 256.0, + "height": 384.0, + "type": "Scheduler", + "properties": { + "topic": "/SmartDashboard/Scheduler", + "period": 0.06 + } + }, + { + "title": "Left Distance (m)", + "x": 0.0, + "y": 512.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Left Distance (m)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Right Distance (m)", + "x": 128.0, + "y": 512.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Right Distance (m)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose X (Meter)", + "x": 1024.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose X (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Y (Meter)", + "x": 1024.0, + "y": 128.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Y (Meter)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Pose Theta (Degrees)", + "x": 1024.0, + "y": 256.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Pose Theta (Degrees)", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/Experimental_Drivetrain_2026/gradlew b/prototype_projects/Experimental_Drivetrain_2026/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/Experimental_Drivetrain_2026/gradlew.bat b/prototype_projects/Experimental_Drivetrain_2026/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/Experimental_Drivetrain_2026/settings.gradle b/prototype_projects/Experimental_Drivetrain_2026/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/example.txt b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/DriveForward.auto b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/DriveForward.auto new file mode 100644 index 0000000..983e8ed --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/DriveForward.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "DriveForward" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto new file mode 100644 index 0000000..62253ac --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/ReverseIntoCorner.auto @@ -0,0 +1,25 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "ReverseIntoCorner" + } + }, + { + "type": "named", + "data": { + "name": "AimToHub" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorizontal.auto b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorizontal.auto new file mode 100644 index 0000000..39521a1 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorizontal.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "Example_Path" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto new file mode 100644 index 0000000..a2e63bd --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/SampleHorziontalBackward.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "ExamplePathBackwards" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/TowerlineUp.auto b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/TowerlineUp.auto new file mode 100644 index 0000000..5ad17a4 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/autos/TowerlineUp.auto @@ -0,0 +1,25 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "LineupToTower" + } + }, + { + "type": "named", + "data": { + "name": "RotateTo-180" + } + } + ] + } + }, + "resetOdom": false, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/navgrid.json b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/navgrid.json new file mode 100644 index 0000000..0c6a2de --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/navgrid.json @@ -0,0 +1 @@ +{"field_size":{"x":16.54,"y":8.07},"nodeSizeMeters":0.3,"grid":[[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]]} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/2_Meter_New.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/2_Meter_New.path new file mode 100644 index 0000000..a1988a4 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/2_Meter_New.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 0.9999999999999996, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 8.0 + }, + "prevControl": { + "x": 1.0, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path new file mode 100644 index 0000000..ee56cf3 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveBackwardToCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.5, + "y": 6.3 + }, + "prevControl": null, + "nextControl": { + "x": 2.749847706754774, + "y": 6.291275125824375 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.5, + "y": 6.3 + }, + "prevControl": { + "x": 0.2501522932452258, + "y": 6.308724874175625 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForward.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForward.path new file mode 100644 index 0000000..c8313ec --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForward.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.293102564102565, + "y": 5.896 + }, + "prevControl": null, + "nextControl": { + "x": 3.293102564102565, + "y": 5.896 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.6070897435897447, + "y": 5.896 + }, + "prevControl": { + "x": 2.6070897435897447, + "y": 5.896 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path new file mode 100644 index 0000000..b806894 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveForwardFromCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.0, + "y": 6.28 + }, + "prevControl": null, + "nextControl": { + "x": 1.2499999048070625, + "y": 6.2797818338711915 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.252435897435897, + "y": 6.28 + }, + "prevControl": { + "x": 2.0028673041421476, + "y": 6.265319494512134 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveToCorner.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveToCorner.path new file mode 100644 index 0000000..07e6691 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/DriveToCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.2582179487179497, + "y": 5.535038461538461 + }, + "prevControl": null, + "nextControl": { + "x": 2.2931025641025653, + "y": 6.058307692307692 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.7, + "y": 6.5 + }, + "prevControl": { + "x": 2.2465897435897455, + "y": 6.139705128205128 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path new file mode 100644 index 0000000..928549c --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ExamplePathBackwards.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 4.587 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 3.814064102564102 + }, + "prevControl": { + "x": 2.0, + "y": 4.064064102564102 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Example_Path.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Example_Path.path new file mode 100644 index 0000000..64d5745 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Example_Path.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 4.587 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 3.814064102564102 + }, + "prevControl": { + "x": 2.0, + "y": 4.064064102564102 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/LineupToTower.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/LineupToTower.path new file mode 100644 index 0000000..56ce83d --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/LineupToTower.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 3.0721923076923088, + "y": 5.116423076923077 + }, + "prevControl": null, + "nextControl": { + "x": 3.130333333333334, + "y": 4.581525641025641 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.7814871794871805, + "y": 3.976858974358975 + }, + "prevControl": { + "x": 3.2001025641025653, + "y": 4.186166666666668 + }, + "nextControl": { + "x": 2.488419497644409, + "y": 3.8303251334375887 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.5721538461538476, + "y": 3.755923076923077 + }, + "prevControl": { + "x": 2.4326410256410247, + "y": 3.767551282051283 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 131.18592516570953 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path new file mode 100644 index 0000000..f5a63f4 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/ReverseIntoCorner.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 1.7349487179487189, + "y": 6.104820512820513 + }, + "prevControl": null, + "nextControl": { + "x": 1.5521849008880018, + "y": 6.275400075410516 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.7930641025641041, + "y": 7.162987179487179 + }, + "prevControl": { + "x": 0.9791153846153859, + "y": 6.942051282051282 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": -62.96913974015706 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path new file mode 100644 index 0000000..b7dff0e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Reverse_S_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": null, + "nextControl": { + "x": 0.43680380386361306, + "y": 6.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": { + "x": 2.0, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": true, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/S_Turn.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/S_Turn.path new file mode 100644 index 0000000..a8a0ad7 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/S_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": { + "x": 0.0, + "y": 6.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Smooth_Turn.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Smooth_Turn.path new file mode 100644 index 0000000..639102e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/Smooth_Turn.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 8.0 + }, + "prevControl": null, + "nextControl": { + "x": 2.0, + "y": 8.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 6.0 + }, + "prevControl": { + "x": 1.9999999999999998, + "y": 8.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wawd.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wawd.path new file mode 100644 index 0000000..8cf8bff --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wawd.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 4.607, + "y": 4.035 + }, + "prevControl": null, + "nextControl": { + "x": 5.606999999999999, + "y": 4.035 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 8.304884615384616, + "y": 4.035 + }, + "prevControl": { + "x": 7.304884615384616, + "y": 4.035 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wdawda.path b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wdawda.path new file mode 100644 index 0000000..74c7aba --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/paths/wdawda.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 0.5953846153846167, + "y": 6.267615384615385 + }, + "prevControl": null, + "nextControl": { + "x": 1.0256282051282062, + "y": 6.27924358974359 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 2.0, + "y": 4.837 + }, + "prevControl": { + "x": 2.0, + "y": 5.355075362944116 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "pointTowardsZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 1.0, + "maxAcceleration": 1.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0, + "nominalVoltage": 12.0, + "unlimited": false + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/settings.json b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/settings.json new file mode 100644 index 0000000..9479112 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/deploy/pathplanner/settings.json @@ -0,0 +1,36 @@ +{ + "robotWidth": 0.9, + "robotLength": 0.9, + "holonomicMode": false, + "pathFolders": [ + "New Folder" + ], + "autoFolders": [], + "defaultMaxVel": 1.0, + "defaultMaxAccel": 1.0, + "defaultMaxAngVel": 540.0, + "defaultMaxAngAccel": 720.0, + "defaultNominalVoltage": 12.0, + "robotMass": 74.088, + "robotMOI": 6.883, + "robotTrackwidth": 0.546, + "driveWheelRadius": 0.0508, + "driveGearing": 5.143, + "maxDriveSpeed": 5.45, + "driveMotorType": "krakenX60", + "driveCurrentLimit": 60.0, + "wheelCOF": 1.0, + "flModuleX": 0.273, + "flModuleY": 0.273, + "frModuleX": 0.273, + "frModuleY": -0.273, + "blModuleX": -0.273, + "blModuleY": 0.273, + "brModuleX": -0.273, + "brModuleY": -0.273, + "bumperOffsetX": 0.0, + "bumperOffsetY": 0.0, + "robotFeatures": [ + "{\"name\":\"Rectangle\",\"type\":\"rounded_rect\",\"data\":{\"center\":{\"x\":0.0,\"y\":0.0},\"size\":{\"width\":0.2,\"length\":0.2},\"borderRadius\":0.05,\"strokeWidth\":0.02,\"filled\":false}}" + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Constants.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..2808f3b --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Constants.java @@ -0,0 +1,96 @@ +// 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 frc.robot; + +import com.pathplanner.lib.config.ModuleConfig; +import com.pathplanner.lib.config.RobotConfig; + +import edu.wpi.first.math.system.plant.DCMotor; + +/** + * 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 class AutoConstants { + public static final double kMaxDriveVelocityMPS = 40.0; // true max speed of the robot mps + public static final double kWheelCOF = 1.0; // no data for this, bsed it + + public static final double kMassKG = 15.0; + + // Uncommenting this causes the weird x y rotion error. + //public static final double kMOI = (1/12) * kMassKG * ((DrivetrainConstants.kLengthMeters*DrivetrainConstants.kLengthMeters) + (DrivetrainConstants.kWidthMeters*DrivetrainConstants.kWidthMeters)); // Moment of Intertia + public static final double kMOI = kMassKG * (DrivetrainConstants.kTrackWidthMeters/2) * (DrivetrainConstants.kA_angular / DrivetrainConstants.kA_linear); // Moment of Intertia + + public static final double kDriveCurrentLimit = 10.0; // amps + + // These values may change per bot, we might want to populate these on init w/ accurate data + public static final int kNumMotors = 4; // # of motors in a gearbox + + public static final DCMotor kDriveMotor = DCMotor.getKrakenX60(kNumMotors); + public static final ModuleConfig kModuleConfig = new ModuleConfig( + DrivetrainConstants.kWheelRadiusMeters, + kMaxDriveVelocityMPS, + kWheelCOF, + kDriveMotor, + kDriveCurrentLimit, + kNumMotors + ); + + public static final RobotConfig kRobotConfig = new RobotConfig(kMassKG, kMOI, kModuleConfig, DrivetrainConstants.kTrackWidthMeters); + // populate on init possibility end here + } + + public static class OperatorConstants { + public static final int kDriverControllerPort = 0; + public static final double kDriverControllerDeadband = 0.02; // Exclusive + } + + public static class DrivetrainConstants { + public static final int kLeftMotorCANID = 10; + public static final int kOptionalLeftMotorCANID = 11; + + public static final int kRightMotorCANID = 20; + public static final int kOptionalRightMotorCANID = 21; + + public static final double kTurnDivider = 2; + public static final double kSpeedDivider = 2.5; + + // These numbers came from the ctre example then tweaked + public static final double kS = 0.1; // A velocity target of 1 rps results in xV output + public static final double kV = 0.12; // Add x V output to overcome static friction + public static final double kP = 0.11; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kA_linear = 0.01; // Voltage needed to induce a given accel. in the motor shaft + public static final double kA_angular = 0.01; // TODO: We need to measure this! + public static final double kPeakVoltage = 8.0; + + public static final double kMaxVelocityMPS = 3.0; // 6 mps is the max of the motors during zero load. + public static final double kMaxAccelerationMPS2 = 5.0; // M/S^2 + + public static final double kMotionMagicAcceleration = 100.0; // Higher number --> Faster (50.0 = ~1s to max) + public static final double kMotionMagicJerk = 4000.0; + + // For Auto Potentially + public static boolean kLeftPositiveMovesForward = true; + public static boolean kRightPositiveMovesForward = true; + + // Physical measurements related to the drivetrain. + public static final double kDrivetrainGearRatio = 0.2; + public static final double kWheelRadiusMeters = (4.0 / 2.0) * 0.0254; // Four Inch Wheels + public static final double kWheelCircumference = 2 * Math.PI * DrivetrainConstants.kWheelRadiusMeters; + public static final double kWidthMeters = 0.760; + public static final double kLengthMeters = 0.760; + public static final double kTrackWidthMeters = 29.0 * 0.0254;//0.74; + + // SmartDashboard update frequency for drive subsystem state in 20ms counts. + public static final int kTicksPerUpdate = 5; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/LimelightHelpers.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/LimelightHelpers.java new file mode 100644 index 0000000..1dbf387 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/LimelightHelpers.java @@ -0,0 +1,1645 @@ +//LimelightHelpers v1.11 (REQUIRES LLOS 2025.0 OR LATER) + +package frc.robot; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonFormat.Shape; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.geometry.Translation3d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.networktables.DoubleArrayEntry; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableEntry; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.TimestampedDoubleArray; + +/** + * LimelightHelpers provides static methods and classes for interfacing with Limelight vision cameras in FRC. + * This library supports all Limelight features including AprilTag tracking, Neural Networks, and standard color/retroreflective tracking. + */ +public class LimelightHelpers { + + private static final Map doubleArrayEntries = new ConcurrentHashMap<>(); + + /** + * Represents a Color/Retroreflective Target Result extracted from JSON Output + */ + public static class LimelightTarget_Retro { + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Retro() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + + } + + /** + * Represents an AprilTag/Fiducial Target Result extracted from JSON Output + */ + public static class LimelightTarget_Fiducial { + + @JsonProperty("fID") + public double fiducialID; + + @JsonProperty("fam") + public String fiducialFamily; + + @JsonProperty("t6c_ts") + private double[] cameraPose_TargetSpace; + + @JsonProperty("t6r_fs") + private double[] robotPose_FieldSpace; + + @JsonProperty("t6r_ts") + private double[] robotPose_TargetSpace; + + @JsonProperty("t6t_cs") + private double[] targetPose_CameraSpace; + + @JsonProperty("t6t_rs") + private double[] targetPose_RobotSpace; + + public Pose3d getCameraPose_TargetSpace() + { + return toPose3D(cameraPose_TargetSpace); + } + public Pose3d getRobotPose_FieldSpace() + { + return toPose3D(robotPose_FieldSpace); + } + public Pose3d getRobotPose_TargetSpace() + { + return toPose3D(robotPose_TargetSpace); + } + public Pose3d getTargetPose_CameraSpace() + { + return toPose3D(targetPose_CameraSpace); + } + public Pose3d getTargetPose_RobotSpace() + { + return toPose3D(targetPose_RobotSpace); + } + + public Pose2d getCameraPose_TargetSpace2D() + { + return toPose2D(cameraPose_TargetSpace); + } + public Pose2d getRobotPose_FieldSpace2D() + { + return toPose2D(robotPose_FieldSpace); + } + public Pose2d getRobotPose_TargetSpace2D() + { + return toPose2D(robotPose_TargetSpace); + } + public Pose2d getTargetPose_CameraSpace2D() + { + return toPose2D(targetPose_CameraSpace); + } + public Pose2d getTargetPose_RobotSpace2D() + { + return toPose2D(targetPose_RobotSpace); + } + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ts") + public double ts; + + public LimelightTarget_Fiducial() { + cameraPose_TargetSpace = new double[6]; + robotPose_FieldSpace = new double[6]; + robotPose_TargetSpace = new double[6]; + targetPose_CameraSpace = new double[6]; + targetPose_RobotSpace = new double[6]; + } + } + + /** + * Represents a Barcode Target Result extracted from JSON Output + */ + public static class LimelightTarget_Barcode { + + /** + * Barcode family type (e.g. "QR", "DataMatrix", etc.) + */ + @JsonProperty("fam") + public String family; + + /** + * Gets the decoded data content of the barcode + */ + @JsonProperty("data") + public String data; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("pts") + public double[][] corners; + + public LimelightTarget_Barcode() { + } + + public String getFamily() { + return family; + } + } + + /** + * Represents a Neural Classifier Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Classifier { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("zone") + public double zone; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("typ") + public double ty_pixels; + + public LimelightTarget_Classifier() { + } + } + + /** + * Represents a Neural Detector Pipeline Result extracted from JSON Output + */ + public static class LimelightTarget_Detector { + + @JsonProperty("class") + public String className; + + @JsonProperty("classID") + public double classID; + + @JsonProperty("conf") + public double confidence; + + @JsonProperty("ta") + public double ta; + + @JsonProperty("tx") + public double tx; + + @JsonProperty("ty") + public double ty; + + @JsonProperty("txp") + public double tx_pixels; + + @JsonProperty("typ") + public double ty_pixels; + + @JsonProperty("tx_nocross") + public double tx_nocrosshair; + + @JsonProperty("ty_nocross") + public double ty_nocrosshair; + + public LimelightTarget_Detector() { + } + } + + /** + * Limelight Results object, parsed from a Limelight's JSON results output. + */ + public static class LimelightResults { + + public String error; + + @JsonProperty("pID") + public double pipelineID; + + @JsonProperty("tl") + public double latency_pipeline; + + @JsonProperty("cl") + public double latency_capture; + + public double latency_jsonParse; + + @JsonProperty("ts") + public double timestamp_LIMELIGHT_publish; + + @JsonProperty("ts_rio") + public double timestamp_RIOFPGA_capture; + + @JsonProperty("v") + @JsonFormat(shape = Shape.NUMBER) + public boolean valid; + + @JsonProperty("botpose") + public double[] botpose; + + @JsonProperty("botpose_wpired") + public double[] botpose_wpired; + + @JsonProperty("botpose_wpiblue") + public double[] botpose_wpiblue; + + @JsonProperty("botpose_tagcount") + public double botpose_tagcount; + + @JsonProperty("botpose_span") + public double botpose_span; + + @JsonProperty("botpose_avgdist") + public double botpose_avgdist; + + @JsonProperty("botpose_avgarea") + public double botpose_avgarea; + + @JsonProperty("t6c_rs") + public double[] camerapose_robotspace; + + public Pose3d getBotPose3d() { + return toPose3D(botpose); + } + + public Pose3d getBotPose3d_wpiRed() { + return toPose3D(botpose_wpired); + } + + public Pose3d getBotPose3d_wpiBlue() { + return toPose3D(botpose_wpiblue); + } + + public Pose2d getBotPose2d() { + return toPose2D(botpose); + } + + public Pose2d getBotPose2d_wpiRed() { + return toPose2D(botpose_wpired); + } + + public Pose2d getBotPose2d_wpiBlue() { + return toPose2D(botpose_wpiblue); + } + + @JsonProperty("Retro") + public LimelightTarget_Retro[] targets_Retro; + + @JsonProperty("Fiducial") + public LimelightTarget_Fiducial[] targets_Fiducials; + + @JsonProperty("Classifier") + public LimelightTarget_Classifier[] targets_Classifier; + + @JsonProperty("Detector") + public LimelightTarget_Detector[] targets_Detector; + + @JsonProperty("Barcode") + public LimelightTarget_Barcode[] targets_Barcode; + + public LimelightResults() { + botpose = new double[6]; + botpose_wpired = new double[6]; + botpose_wpiblue = new double[6]; + camerapose_robotspace = new double[6]; + targets_Retro = new LimelightTarget_Retro[0]; + targets_Fiducials = new LimelightTarget_Fiducial[0]; + targets_Classifier = new LimelightTarget_Classifier[0]; + targets_Detector = new LimelightTarget_Detector[0]; + targets_Barcode = new LimelightTarget_Barcode[0]; + + } + + + } + + /** + * Represents a Limelight Raw Fiducial result from Limelight's NetworkTables output. + */ + public static class RawFiducial { + public int id = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double distToCamera = 0; + public double distToRobot = 0; + public double ambiguity = 0; + + + public RawFiducial(int id, double txnc, double tync, double ta, double distToCamera, double distToRobot, double ambiguity) { + this.id = id; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.distToCamera = distToCamera; + this.distToRobot = distToRobot; + this.ambiguity = ambiguity; + } + } + + /** + * Represents a Limelight Raw Neural Detector result from Limelight's NetworkTables output. + */ + public static class RawDetection { + public int classId = 0; + public double txnc = 0; + public double tync = 0; + public double ta = 0; + public double corner0_X = 0; + public double corner0_Y = 0; + public double corner1_X = 0; + public double corner1_Y = 0; + public double corner2_X = 0; + public double corner2_Y = 0; + public double corner3_X = 0; + public double corner3_Y = 0; + + + public RawDetection(int classId, double txnc, double tync, double ta, + double corner0_X, double corner0_Y, + double corner1_X, double corner1_Y, + double corner2_X, double corner2_Y, + double corner3_X, double corner3_Y ) { + this.classId = classId; + this.txnc = txnc; + this.tync = tync; + this.ta = ta; + this.corner0_X = corner0_X; + this.corner0_Y = corner0_Y; + this.corner1_X = corner1_X; + this.corner1_Y = corner1_Y; + this.corner2_X = corner2_X; + this.corner2_Y = corner2_Y; + this.corner3_X = corner3_X; + this.corner3_Y = corner3_Y; + } + } + + /** + * Represents a 3D Pose Estimate. + */ + public static class PoseEstimate { + public Pose2d pose; + public double timestampSeconds; + public double latency; + public int tagCount; + public double tagSpan; + public double avgTagDist; + public double avgTagArea; + + public RawFiducial[] rawFiducials; + public boolean isMegaTag2; + + /** + * Instantiates a PoseEstimate object with default values + */ + public PoseEstimate() { + this.pose = new Pose2d(); + this.timestampSeconds = 0; + this.latency = 0; + this.tagCount = 0; + this.tagSpan = 0; + this.avgTagDist = 0; + this.avgTagArea = 0; + this.rawFiducials = new RawFiducial[]{}; + this.isMegaTag2 = false; + } + + public PoseEstimate(Pose2d pose, double timestampSeconds, double latency, + int tagCount, double tagSpan, double avgTagDist, + double avgTagArea, RawFiducial[] rawFiducials, boolean isMegaTag2) { + + this.pose = pose; + this.timestampSeconds = timestampSeconds; + this.latency = latency; + this.tagCount = tagCount; + this.tagSpan = tagSpan; + this.avgTagDist = avgTagDist; + this.avgTagArea = avgTagArea; + this.rawFiducials = rawFiducials; + this.isMegaTag2 = isMegaTag2; + } + + } + + /** + * Encapsulates the state of an internal Limelight IMU. + */ + public static class IMUData { + public double robotYaw = 0.0; + public double Roll = 0.0; + public double Pitch = 0.0; + public double Yaw = 0.0; + public double gyroX = 0.0; + public double gyroY = 0.0; + public double gyroZ = 0.0; + public double accelX = 0.0; + public double accelY = 0.0; + public double accelZ = 0.0; + + public IMUData() {} + + public IMUData(double[] imuData) { + if (imuData != null && imuData.length >= 10) { + this.robotYaw = imuData[0]; + this.Roll = imuData[1]; + this.Pitch = imuData[2]; + this.Yaw = imuData[3]; + this.gyroX = imuData[4]; + this.gyroY = imuData[5]; + this.gyroZ = imuData[6]; + this.accelX = imuData[7]; + this.accelY = imuData[8]; + this.accelZ = imuData[9]; + } + } + } + + + private static ObjectMapper mapper; + + /** + * Print JSON Parse time to the console in milliseconds + */ + static boolean profileJSON = false; + + static final String sanitizeName(String name) { + if (name == "" || name == null) { + return "limelight"; + } + return name; + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose3d object. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose3d object representing the pose, or empty Pose3d if invalid data + */ + public static Pose3d toPose3D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 3D Pose Data!"); + return new Pose3d(); + } + return new Pose3d( + new Translation3d(inData[0], inData[1], inData[2]), + new Rotation3d(Units.degreesToRadians(inData[3]), Units.degreesToRadians(inData[4]), + Units.degreesToRadians(inData[5]))); + } + + /** + * Takes a 6-length array of pose data and converts it to a Pose2d object. + * Uses only x, y, and yaw components, ignoring z, roll, and pitch. + * Array format: [x, y, z, roll, pitch, yaw] where angles are in degrees. + * @param inData Array containing pose data [x, y, z, roll, pitch, yaw] + * @return Pose2d object representing the pose, or empty Pose2d if invalid data + */ + public static Pose2d toPose2D(double[] inData){ + if(inData.length < 6) + { + //System.err.println("Bad LL 2D Pose Data!"); + return new Pose2d(); + } + Translation2d tran2d = new Translation2d(inData[0], inData[1]); + Rotation2d r2d = new Rotation2d(Units.degreesToRadians(inData[5])); + return new Pose2d(tran2d, r2d); + } + + /** + * Converts a Pose3d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * + * @param pose The Pose3d object to convert + * @return A 6-element array containing [x, y, z, roll, pitch, yaw] + */ + public static double[] pose3dToArray(Pose3d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = pose.getTranslation().getZ(); + result[3] = Units.radiansToDegrees(pose.getRotation().getX()); + result[4] = Units.radiansToDegrees(pose.getRotation().getY()); + result[5] = Units.radiansToDegrees(pose.getRotation().getZ()); + return result; + } + + /** + * Converts a Pose2d object to an array of doubles in the format [x, y, z, roll, pitch, yaw]. + * Translation components are in meters, rotation components are in degrees. + * Note: z, roll, and pitch will be 0 since Pose2d only contains x, y, and yaw. + * + * @param pose The Pose2d object to convert + * @return A 6-element array containing [x, y, 0, 0, 0, yaw] + */ + public static double[] pose2dToArray(Pose2d pose) { + double[] result = new double[6]; + result[0] = pose.getTranslation().getX(); + result[1] = pose.getTranslation().getY(); + result[2] = 0; + result[3] = Units.radiansToDegrees(0); + result[4] = Units.radiansToDegrees(0); + result[5] = Units.radiansToDegrees(pose.getRotation().getRadians()); + return result; + } + + private static double extractArrayEntry(double[] inData, int position){ + if(inData.length < position+1) + { + return 0; + } + return inData[position]; + } + + private static PoseEstimate getBotPoseEstimate(String limelightName, String entryName, boolean isMegaTag2) { + DoubleArrayEntry poseEntry = LimelightHelpers.getLimelightDoubleArrayEntry(limelightName, entryName); + + TimestampedDoubleArray tsValue = poseEntry.getAtomic(); + double[] poseArray = tsValue.value; + long timestamp = tsValue.timestamp; + + if (poseArray.length == 0) { + // Handle the case where no data is available + return null; // or some default PoseEstimate + } + + var pose = toPose2D(poseArray); + double latency = extractArrayEntry(poseArray, 6); + int tagCount = (int)extractArrayEntry(poseArray, 7); + double tagSpan = extractArrayEntry(poseArray, 8); + double tagDist = extractArrayEntry(poseArray, 9); + double tagArea = extractArrayEntry(poseArray, 10); + + // Convert server timestamp from microseconds to seconds and adjust for latency + double adjustedTimestamp = (timestamp / 1000000.0) - (latency / 1000.0); + + RawFiducial[] rawFiducials = new RawFiducial[tagCount]; + int valsPerFiducial = 7; + int expectedTotalVals = 11 + valsPerFiducial * tagCount; + + if (poseArray.length != expectedTotalVals) { + // Don't populate fiducials + } else { + for(int i = 0; i < tagCount; i++) { + int baseIndex = 11 + (i * valsPerFiducial); + int id = (int)poseArray[baseIndex]; + double txnc = poseArray[baseIndex + 1]; + double tync = poseArray[baseIndex + 2]; + double ta = poseArray[baseIndex + 3]; + double distToCamera = poseArray[baseIndex + 4]; + double distToRobot = poseArray[baseIndex + 5]; + double ambiguity = poseArray[baseIndex + 6]; + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + } + + return new PoseEstimate(pose, adjustedTimestamp, latency, tagCount, tagSpan, tagDist, tagArea, rawFiducials, isMegaTag2); + } + + /** + * Gets the latest raw fiducial/AprilTag detection results from NetworkTables. + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawFiducial objects containing detection details + */ + public static RawFiducial[] getRawFiducials(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawfiducials"); + var rawFiducialArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 7; + if (rawFiducialArray.length % valsPerEntry != 0) { + return new RawFiducial[0]; + } + + int numFiducials = rawFiducialArray.length / valsPerEntry; + RawFiducial[] rawFiducials = new RawFiducial[numFiducials]; + + for (int i = 0; i < numFiducials; i++) { + int baseIndex = i * valsPerEntry; + int id = (int) extractArrayEntry(rawFiducialArray, baseIndex); + double txnc = extractArrayEntry(rawFiducialArray, baseIndex + 1); + double tync = extractArrayEntry(rawFiducialArray, baseIndex + 2); + double ta = extractArrayEntry(rawFiducialArray, baseIndex + 3); + double distToCamera = extractArrayEntry(rawFiducialArray, baseIndex + 4); + double distToRobot = extractArrayEntry(rawFiducialArray, baseIndex + 5); + double ambiguity = extractArrayEntry(rawFiducialArray, baseIndex + 6); + + rawFiducials[i] = new RawFiducial(id, txnc, tync, ta, distToCamera, distToRobot, ambiguity); + } + + return rawFiducials; + } + + /** + * Gets the latest raw neural detector results from NetworkTables + * + * @param limelightName Name/identifier of the Limelight + * @return Array of RawDetection objects containing detection details + */ + public static RawDetection[] getRawDetections(String limelightName) { + var entry = LimelightHelpers.getLimelightNTTableEntry(limelightName, "rawdetections"); + var rawDetectionArray = entry.getDoubleArray(new double[0]); + int valsPerEntry = 12; + if (rawDetectionArray.length % valsPerEntry != 0) { + return new RawDetection[0]; + } + + int numDetections = rawDetectionArray.length / valsPerEntry; + RawDetection[] rawDetections = new RawDetection[numDetections]; + + for (int i = 0; i < numDetections; i++) { + int baseIndex = i * valsPerEntry; // Starting index for this detection's data + int classId = (int) extractArrayEntry(rawDetectionArray, baseIndex); + double txnc = extractArrayEntry(rawDetectionArray, baseIndex + 1); + double tync = extractArrayEntry(rawDetectionArray, baseIndex + 2); + double ta = extractArrayEntry(rawDetectionArray, baseIndex + 3); + double corner0_X = extractArrayEntry(rawDetectionArray, baseIndex + 4); + double corner0_Y = extractArrayEntry(rawDetectionArray, baseIndex + 5); + double corner1_X = extractArrayEntry(rawDetectionArray, baseIndex + 6); + double corner1_Y = extractArrayEntry(rawDetectionArray, baseIndex + 7); + double corner2_X = extractArrayEntry(rawDetectionArray, baseIndex + 8); + double corner2_Y = extractArrayEntry(rawDetectionArray, baseIndex + 9); + double corner3_X = extractArrayEntry(rawDetectionArray, baseIndex + 10); + double corner3_Y = extractArrayEntry(rawDetectionArray, baseIndex + 11); + + rawDetections[i] = new RawDetection(classId, txnc, tync, ta, corner0_X, corner0_Y, corner1_X, corner1_Y, corner2_X, corner2_Y, corner3_X, corner3_Y); + } + + return rawDetections; + } + + /** + * Prints detailed information about a PoseEstimate to standard output. + * Includes timestamp, latency, tag count, tag span, average tag distance, + * average tag area, and detailed information about each detected fiducial. + * + * @param pose The PoseEstimate object to print. If null, prints "No PoseEstimate available." + */ + public static void printPoseEstimate(PoseEstimate pose) { + if (pose == null) { + System.out.println("No PoseEstimate available."); + return; + } + + System.out.printf("Pose Estimate Information:%n"); + System.out.printf("Timestamp (Seconds): %.3f%n", pose.timestampSeconds); + System.out.printf("Latency: %.3f ms%n", pose.latency); + System.out.printf("Tag Count: %d%n", pose.tagCount); + System.out.printf("Tag Span: %.2f meters%n", pose.tagSpan); + System.out.printf("Average Tag Distance: %.2f meters%n", pose.avgTagDist); + System.out.printf("Average Tag Area: %.2f%% of image%n", pose.avgTagArea); + System.out.printf("Is MegaTag2: %b%n", pose.isMegaTag2); + System.out.println(); + + if (pose.rawFiducials == null || pose.rawFiducials.length == 0) { + System.out.println("No RawFiducials data available."); + return; + } + + System.out.println("Raw Fiducials Details:"); + for (int i = 0; i < pose.rawFiducials.length; i++) { + RawFiducial fiducial = pose.rawFiducials[i]; + System.out.printf(" Fiducial #%d:%n", i + 1); + System.out.printf(" ID: %d%n", fiducial.id); + System.out.printf(" TXNC: %.2f%n", fiducial.txnc); + System.out.printf(" TYNC: %.2f%n", fiducial.tync); + System.out.printf(" TA: %.2f%n", fiducial.ta); + System.out.printf(" Distance to Camera: %.2f meters%n", fiducial.distToCamera); + System.out.printf(" Distance to Robot: %.2f meters%n", fiducial.distToRobot); + System.out.printf(" Ambiguity: %.2f%n", fiducial.ambiguity); + System.out.println(); + } + } + + public static Boolean validPoseEstimate(PoseEstimate pose) { + return pose != null && pose.rawFiducials != null && pose.rawFiducials.length != 0; + } + + public static NetworkTable getLimelightNTTable(String tableName) { + return NetworkTableInstance.getDefault().getTable(sanitizeName(tableName)); + } + + public static void Flush() { + NetworkTableInstance.getDefault().flush(); + } + + public static NetworkTableEntry getLimelightNTTableEntry(String tableName, String entryName) { + return getLimelightNTTable(tableName).getEntry(entryName); + } + + public static DoubleArrayEntry getLimelightDoubleArrayEntry(String tableName, String entryName) { + String key = tableName + "/" + entryName; + return doubleArrayEntries.computeIfAbsent(key, k -> { + NetworkTable table = getLimelightNTTable(tableName); + return table.getDoubleArrayTopic(entryName).getEntry(new double[0]); + }); + } + + public static double getLimelightNTDouble(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDouble(0.0); + } + + public static void setLimelightNTDouble(String tableName, String entryName, double val) { + getLimelightNTTableEntry(tableName, entryName).setDouble(val); + } + + public static void setLimelightNTDoubleArray(String tableName, String entryName, double[] val) { + getLimelightNTTableEntry(tableName, entryName).setDoubleArray(val); + } + + public static double[] getLimelightNTDoubleArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getDoubleArray(new double[0]); + } + + + public static String getLimelightNTString(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getString(""); + } + + public static String[] getLimelightNTStringArray(String tableName, String entryName) { + return getLimelightNTTableEntry(tableName, entryName).getStringArray(new String[0]); + } + + + public static URL getLimelightURLString(String tableName, String request) { + String urlString = "http://" + sanitizeName(tableName) + ".local:5807/" + request; + URL url; + try { + url = new URL(urlString); + return url; + } catch (MalformedURLException e) { + System.err.println("bad LL URL"); + } + return null; + } + ///// + ///// + + /** + * Does the Limelight have a valid target? + * @param limelightName Name of the Limelight camera ("" for default) + * @return True if a valid target is present, false otherwise + */ + public static boolean getTV(String limelightName) { + return 1.0 == getLimelightNTDouble(limelightName, "tv"); + } + + /** + * Gets the horizontal offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTX(String limelightName) { + return getLimelightNTDouble(limelightName, "tx"); + } + + /** + * Gets the vertical offset from the crosshair to the target in degrees. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTY(String limelightName) { + return getLimelightNTDouble(limelightName, "ty"); + } + + /** + * Gets the horizontal offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Horizontal offset angle in degrees + */ + public static double getTXNC(String limelightName) { + return getLimelightNTDouble(limelightName, "txnc"); + } + + /** + * Gets the vertical offset from the principal pixel/point to the target in degrees. This is the most accurate 2d metric if you are using a calibrated camera and you don't need adjustable crosshair functionality. + * @param limelightName Name of the Limelight camera ("" for default) + * @return Vertical offset angle in degrees + */ + public static double getTYNC(String limelightName) { + return getLimelightNTDouble(limelightName, "tync"); + } + + /** + * Gets the target area as a percentage of the image (0-100%). + * @param limelightName Name of the Limelight camera ("" for default) + * @return Target area percentage (0-100) + */ + public static double getTA(String limelightName) { + return getLimelightNTDouble(limelightName, "ta"); + } + + /** + * T2D is an array that contains several targeting metrcis + * @param limelightName Name of the Limelight camera + * @return Array containing [targetValid, targetCount, targetLatency, captureLatency, tx, ty, txnc, tync, ta, tid, targetClassIndexDetector, + * targetClassIndexClassifier, targetLongSidePixels, targetShortSidePixels, targetHorizontalExtentPixels, targetVerticalExtentPixels, targetSkewDegrees] + */ + public static double[] getT2DArray(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "t2d"); + } + + /** + * Gets the number of targets currently detected. + * @param limelightName Name of the Limelight camera + * @return Number of detected targets + */ + public static int getTargetCount(String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[1]; + } + return 0; + } + + /** + * Gets the classifier class index from the currently running neural classifier pipeline + * @param limelightName Name of the Limelight camera + * @return Class index from classifier pipeline + */ + public static int getClassifierClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[10]; + } + return 0; + } + + /** + * Gets the detector class index from the primary result of the currently running neural detector pipeline. + * @param limelightName Name of the Limelight camera + * @return Class index from detector pipeline + */ + public static int getDetectorClassIndex (String limelightName) { + double[] t2d = getT2DArray(limelightName); + if(t2d.length == 17) + { + return (int)t2d[11]; + } + return 0; + } + + /** + * Gets the current neural classifier result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from classifier pipeline + */ + public static String getClassifierClass (String limelightName) { + return getLimelightNTString(limelightName, "tcclass"); + } + + /** + * Gets the primary neural detector result class name. + * @param limelightName Name of the Limelight camera + * @return Class name string from detector pipeline + */ + public static String getDetectorClass (String limelightName) { + return getLimelightNTString(limelightName, "tdclass"); + } + + /** + * Gets the pipeline's processing latency contribution. + * @param limelightName Name of the Limelight camera + * @return Pipeline latency in milliseconds + */ + public static double getLatency_Pipeline(String limelightName) { + return getLimelightNTDouble(limelightName, "tl"); + } + + /** + * Gets the capture latency. + * @param limelightName Name of the Limelight camera + * @return Capture latency in milliseconds + */ + public static double getLatency_Capture(String limelightName) { + return getLimelightNTDouble(limelightName, "cl"); + } + + /** + * Gets the active pipeline index. + * @param limelightName Name of the Limelight camera + * @return Current pipeline index (0-9) + */ + public static double getCurrentPipelineIndex(String limelightName) { + return getLimelightNTDouble(limelightName, "getpipe"); + } + + /** + * Gets the current pipeline type. + * @param limelightName Name of the Limelight camera + * @return Pipeline type string (e.g. "retro", "apriltag", etc) + */ + public static String getCurrentPipelineType(String limelightName) { + return getLimelightNTString(limelightName, "getpipetype"); + } + + /** + * Gets the full JSON results dump. + * @param limelightName Name of the Limelight camera + * @return JSON string containing all current results + */ + public static String getJSONDump(String limelightName) { + return getLimelightNTString(limelightName, "json"); + } + + /** + * Switch to getBotPose + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + /** + * Switch to getBotPose_wpiRed + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + /** + * Switch to getBotPose_wpiBlue + * + * @param limelightName + * @return + */ + @Deprecated + public static double[] getBotpose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose"); + } + + public static double[] getBotPose_wpiRed(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + } + + public static double[] getBotPose_wpiBlue(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + } + + public static double[] getBotPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + } + + public static double[] getCameraPose_TargetSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + } + + public static double[] getTargetPose_CameraSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + } + + public static double[] getTargetPose_RobotSpace(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + } + + public static double[] getTargetColor(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "tc"); + } + + public static double getFiducialID(String limelightName) { + return getLimelightNTDouble(limelightName, "tid"); + } + + public static String getNeuralClassID(String limelightName) { + return getLimelightNTString(limelightName, "tclass"); + } + + public static String[] getRawBarcodeData(String limelightName) { + return getLimelightNTStringArray(limelightName, "rawbarcodes"); + } + + ///// + ///// + + public static Pose3d getBotPose3d(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose"); + return toPose3D(poseArray); + } + + /** + * (Not Recommended) Gets the robot's 3D pose in the WPILib Red Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Red Alliance field space + */ + public static Pose3d getBotPose3d_wpiRed(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpired"); + return toPose3D(poseArray); + } + + /** + * (Recommended) Gets the robot's 3D pose in the WPILib Blue Alliance Coordinate System. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation in Blue Alliance field space + */ + public static Pose3d getBotPose3d_wpiBlue(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_wpiblue"); + return toPose3D(poseArray); + } + + /** + * Gets the robot's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the robot's position and orientation relative to the target + */ + public static Pose3d getBotPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "botpose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the currently tracked target's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the target + */ + public static Pose3d getCameraPose3d_TargetSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_targetspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the camera's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the camera + */ + public static Pose3d getTargetPose3d_CameraSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_cameraspace"); + return toPose3D(poseArray); + } + + /** + * Gets the target's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the target's position and orientation relative to the robot + */ + public static Pose3d getTargetPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "targetpose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the camera's 3D pose with respect to the robot's coordinate system. + * @param limelightName Name/identifier of the Limelight + * @return Pose3d object representing the camera's position and orientation relative to the robot + */ + public static Pose3d getCameraPose3d_RobotSpace(String limelightName) { + double[] poseArray = getLimelightNTDoubleArray(limelightName, "camerapose_robotspace"); + return toPose3D(poseArray); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiBlue(String limelightName) { + + double[] result = getBotPose_wpiBlue(limelightName); + return toPose2D(result); + } + + /** + * Gets the MegaTag1 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpiblue", false); + } + + /** + * Gets the MegaTag2 Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) in the WPILib Blue alliance coordinate system. + * Make sure you are calling setRobotOrientation() before calling this method. + * + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiBlue_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpiblue", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d_wpiRed(String limelightName) { + + double[] result = getBotPose_wpiRed(limelightName); + return toPose2D(result); + + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_wpired", false); + } + + /** + * Gets the Pose2d and timestamp for use with WPILib pose estimator (addVisionMeasurement) when you are on the RED + * alliance + * @param limelightName + * @return + */ + public static PoseEstimate getBotPoseEstimate_wpiRed_MegaTag2(String limelightName) { + return getBotPoseEstimate(limelightName, "botpose_orb_wpired", true); + } + + /** + * Gets the Pose2d for easy use with Odometry vision pose estimator + * (addVisionMeasurement) + * + * @param limelightName + * @return + */ + public static Pose2d getBotPose2d(String limelightName) { + + double[] result = getBotPose(limelightName); + return toPose2D(result); + + } + + /** + * Gets the current IMU data from NetworkTables. + * IMU data is formatted as [robotYaw, Roll, Pitch, Yaw, gyroX, gyroY, gyroZ, accelX, accelY, accelZ]. + * Returns all zeros if data is invalid or unavailable. + * + * @param limelightName Name/identifier of the Limelight + * @return IMUData object containing all current IMU data + */ + public static IMUData getIMUData(String limelightName) { + double[] imuData = getLimelightNTDoubleArray(limelightName, "imu"); + if (imuData == null || imuData.length < 10) { + return new IMUData(); // Returns object with all zeros + } + return new IMUData(imuData); + } + + ///// + ///// + + public static void setPipelineIndex(String limelightName, int pipelineIndex) { + setLimelightNTDouble(limelightName, "pipeline", pipelineIndex); + } + + + public static void setPriorityTagID(String limelightName, int ID) { + setLimelightNTDouble(limelightName, "priorityid", ID); + } + + /** + * Sets LED mode to be controlled by the current pipeline. + * @param limelightName Name of the Limelight camera + */ + public static void setLEDMode_PipelineControl(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 0); + } + + public static void setLEDMode_ForceOff(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 1); + } + + public static void setLEDMode_ForceBlink(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 2); + } + + public static void setLEDMode_ForceOn(String limelightName) { + setLimelightNTDouble(limelightName, "ledMode", 3); + } + + /** + * Enables standard side-by-side stream mode. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_Standard(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 0); + } + + /** + * Enables Picture-in-Picture mode with secondary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPMain(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 1); + } + + /** + * Enables Picture-in-Picture mode with primary stream in the corner. + * @param limelightName Name of the Limelight camera + */ + public static void setStreamMode_PiPSecondary(String limelightName) { + setLimelightNTDouble(limelightName, "stream", 2); + } + + + /** + * Sets the crop window for the camera. The crop window in the UI must be completely open. + * @param limelightName Name of the Limelight camera + * @param cropXMin Minimum X value (-1 to 1) + * @param cropXMax Maximum X value (-1 to 1) + * @param cropYMin Minimum Y value (-1 to 1) + * @param cropYMax Maximum Y value (-1 to 1) + */ + public static void setCropWindow(String limelightName, double cropXMin, double cropXMax, double cropYMin, double cropYMax) { + double[] entries = new double[4]; + entries[0] = cropXMin; + entries[1] = cropXMax; + entries[2] = cropYMin; + entries[3] = cropYMax; + setLimelightNTDoubleArray(limelightName, "crop", entries); + } + + /** + * Sets 3D offset point for easy 3D targeting. + */ + public static void setFiducial3DOffset(String limelightName, double offsetX, double offsetY, double offsetZ) { + double[] entries = new double[3]; + entries[0] = offsetX; + entries[1] = offsetY; + entries[2] = offsetZ; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Sets robot orientation values used by MegaTag2 localization algorithm. + * + * @param limelightName Name/identifier of the Limelight + * @param yaw Robot yaw in degrees. 0 = robot facing red alliance wall in FRC + * @param yawRate (Unnecessary) Angular velocity of robot yaw in degrees per second + * @param pitch (Unnecessary) Robot pitch in degrees + * @param pitchRate (Unnecessary) Angular velocity of robot pitch in degrees per second + * @param roll (Unnecessary) Robot roll in degrees + * @param rollRate (Unnecessary) Angular velocity of robot roll in degrees per second + */ + public static void SetRobotOrientation(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, true); + } + + public static void SetRobotOrientation_NoFlush(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate) { + SetRobotOrientation_INTERNAL(limelightName, yaw, yawRate, pitch, pitchRate, roll, rollRate, false); + } + + private static void SetRobotOrientation_INTERNAL(String limelightName, double yaw, double yawRate, + double pitch, double pitchRate, + double roll, double rollRate, boolean flush) { + + double[] entries = new double[6]; + entries[0] = yaw; + entries[1] = yawRate; + entries[2] = pitch; + entries[3] = pitchRate; + entries[4] = roll; + entries[5] = rollRate; + setLimelightNTDoubleArray(limelightName, "robot_orientation_set", entries); + if(flush) + { + Flush(); + } + } + + /** + * Configures the IMU mode for MegaTag2 Localization + * + * @param limelightName Name/identifier of the Limelight + * @param mode IMU mode. + */ + public static void SetIMUMode(String limelightName, int mode) { + setLimelightNTDouble(limelightName, "imumode_set", mode); + } + + /** + * Sets the 3D point-of-interest offset for the current fiducial pipeline. + * https://docs.limelightvision.io/docs/docs-limelight/pipeline-apriltag/apriltag-3d#point-of-interest-tracking + * + * @param limelightName Name/identifier of the Limelight + * @param x X offset in meters + * @param y Y offset in meters + * @param z Z offset in meters + */ + public static void SetFidcuial3DOffset(String limelightName, double x, double y, + double z) { + + double[] entries = new double[3]; + entries[0] = x; + entries[1] = y; + entries[2] = z; + setLimelightNTDoubleArray(limelightName, "fiducial_offset_set", entries); + } + + /** + * Overrides the valid AprilTag IDs that will be used for localization. + * Tags not in this list will be ignored for robot pose estimation. + * + * @param limelightName Name/identifier of the Limelight + * @param validIDs Array of valid AprilTag IDs to track + */ + public static void SetFiducialIDFiltersOverride(String limelightName, int[] validIDs) { + double[] validIDsDouble = new double[validIDs.length]; + for (int i = 0; i < validIDs.length; i++) { + validIDsDouble[i] = validIDs[i]; + } + setLimelightNTDoubleArray(limelightName, "fiducial_id_filters_set", validIDsDouble); + } + + /** + * Sets the downscaling factor for AprilTag detection. + * Increasing downscale can improve performance at the cost of potentially reduced detection range. + * + * @param limelightName Name/identifier of the Limelight + * @param downscale Downscale factor. Valid values: 1.0 (no downscale), 1.5, 2.0, 3.0, 4.0. Set to 0 for pipeline control. + */ + public static void SetFiducialDownscalingOverride(String limelightName, float downscale) + { + int d = 0; // pipeline + if (downscale == 1.0) + { + d = 1; + } + if (downscale == 1.5) + { + d = 2; + } + if (downscale == 2) + { + d = 3; + } + if (downscale == 3) + { + d = 4; + } + if (downscale == 4) + { + d = 5; + } + setLimelightNTDouble(limelightName, "fiducial_downscale_set", d); + } + + /** + * Sets the camera pose relative to the robot. + * @param limelightName Name of the Limelight camera + * @param forward Forward offset in meters + * @param side Side offset in meters + * @param up Up offset in meters + * @param roll Roll angle in degrees + * @param pitch Pitch angle in degrees + * @param yaw Yaw angle in degrees + */ + public static void setCameraPose_RobotSpace(String limelightName, double forward, double side, double up, double roll, double pitch, double yaw) { + double[] entries = new double[6]; + entries[0] = forward; + entries[1] = side; + entries[2] = up; + entries[3] = roll; + entries[4] = pitch; + entries[5] = yaw; + setLimelightNTDoubleArray(limelightName, "camerapose_robotspace_set", entries); + } + + ///// + ///// + + public static void setPythonScriptData(String limelightName, double[] outgoingPythonData) { + setLimelightNTDoubleArray(limelightName, "llrobot", outgoingPythonData); + } + + public static double[] getPythonScriptData(String limelightName) { + return getLimelightNTDoubleArray(limelightName, "llpython"); + } + + ///// + ///// + + /** + * Asynchronously take snapshot. + */ + public static CompletableFuture takeSnapshot(String tableName, String snapshotName) { + return CompletableFuture.supplyAsync(() -> { + return SYNCH_TAKESNAPSHOT(tableName, snapshotName); + }); + } + + private static boolean SYNCH_TAKESNAPSHOT(String tableName, String snapshotName) { + URL url = getLimelightURLString(tableName, "capturesnapshot"); + try { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + if (snapshotName != null && snapshotName != "") { + connection.setRequestProperty("snapname", snapshotName); + } + + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + return true; + } else { + System.err.println("Bad LL Request"); + } + } catch (IOException e) { + System.err.println(e.getMessage()); + } + return false; + } + + /** + * Gets the latest JSON results output and returns a LimelightResults object. + * @param limelightName Name of the Limelight camera + * @return LimelightResults object containing all current target data + */ + public static LimelightResults getLatestResults(String limelightName) { + + long start = System.nanoTime(); + LimelightHelpers.LimelightResults results = new LimelightHelpers.LimelightResults(); + if (mapper == null) { + mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + try { + results = mapper.readValue(getJSONDump(limelightName), LimelightResults.class); + } catch (JsonProcessingException e) { + results.error = "lljson error: " + e.getMessage(); + } + + long end = System.nanoTime(); + double millis = (end - start) * .000001; + results.latency_jsonParse = millis; + if (profileJSON) { + System.out.printf("lljson: %.2f\r\n", millis); + } + + return results; + } +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Main.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Robot.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..e0d27fa --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/Robot.java @@ -0,0 +1,119 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.TimedRobot; +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 functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private Command m_autonomousCommand; + + private RobotContainer m_robotContainer; + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + @Override + public void robotInit() { + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); + m_robotContainer.InitSmartDashboard(); + } + + /** + * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics + * that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @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(); + + // Send subsystem and robot state back to the driver station. + m_robotContainer.UpdateSmartDashboard(); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() { + m_robotContainer.disabledInit(); + } + + @Override + public void disabledPeriodic() { + LimelightHelpers.SetIMUMode("limelight", 3); + m_robotContainer.disabledPeriodic(); + } + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + m_robotContainer.enabledInit(); + m_robotContainer.autoInit(); + + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); + + // schedule the autonomous command (example) + if (m_autonomousCommand != null) { + m_autonomousCommand.schedule(); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + + m_robotContainer.enabledInit(); + + m_robotContainer.teleopInit(); + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} + + /** This function is called once when the robot is first started up. */ + @Override + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/RobotContainer.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..41e0271 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,331 @@ +// 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 frc.robot; + +import java.util.ArrayList; +import java.util.Optional; + +import com.ctre.phoenix6.hardware.TalonFX; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.auto.NamedCommands; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.pathplanner.lib.path.PathConstraints; +import com.studica.frc.AHRS; +import com.studica.frc.AHRS.NavXComType; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.DriverStation.Alliance; +import edu.wpi.first.wpilibj.smartdashboard.Field2d; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.InstantCommand; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.OperatorConstants; +import frc.robot.commands.AimAtHub; +import frc.robot.commands.DriveForwardFromPos; +import frc.robot.commands.POTFtoPoint; +import frc.robot.commands.RotateToRotation2D; +import frc.robot.factorys.DriveSubsystemFactory; +import frc.robot.factorys.TalonFXFactory; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.subsystems.LimelightSubsystem; +import frc.robot.utils.AutoCommands; + +/** + * 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 trigger mappings) should be declared here. + */ +public class RobotContainer { + // The robot's subsystems and commands are defined here... + private final Optional m_driveSubsystem; + private final LimelightSubsystem m_limelightSubsystem; + + // Replace with CommandPS4Controller or CommandJoystick if needed + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + + private final AHRS m_gyro = new AHRS(NavXComType.kMXP_SPI); + private final DifferentialDriveKinematics m_DriveKinematics = new DifferentialDriveKinematics(DrivetrainConstants.kTrackWidthMeters); + + // A Static Standard Deviation, in the form of [x, y, theta]ᵀ in meters and radians. + private Vector m_drivetrainError = VecBuilder.fill(0.2, 0.2, 0); + private Vector m_limelightError = VecBuilder.fill(.7,.7,9999999); + + private DifferentialDrivePoseEstimator m_PoseEstimator = new DifferentialDrivePoseEstimator( + m_DriveKinematics, + Rotation2d.fromDegrees(0.0), + 0, + 0, + new Pose2d(0.0, 8.0, new Rotation2d()), + m_drivetrainError, + m_limelightError + ); + + PathConstraints m_constraints = new PathConstraints( + 1.0, + 1.0, + 2 * Math.PI, + 4 * Math.PI + ); // The constraints for this path. + + // Smartdashboard Objects + private SendableChooser m_autoChooser; + private final Field2d m_field = new Field2d(); + + // Factorys + private TalonFXFactory m_TalonFXFactory = new TalonFXFactory(); + private DriveSubsystemFactory m_DriveSubsystemFactory = new DriveSubsystemFactory(); + + // Keep track of time for SmartDashboard updates. + static int m_iTickCount = 0; + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + // Init DriveSubsystem + Optional rightLead = m_TalonFXFactory.construct(DrivetrainConstants.kRightMotorCANID); + Optional leftLead = m_TalonFXFactory.construct(DrivetrainConstants.kLeftMotorCANID); + Optional rightFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalRightMotorCANID); + Optional leftFollower = m_TalonFXFactory.construct(DrivetrainConstants.kOptionalLeftMotorCANID); + m_driveSubsystem = m_DriveSubsystemFactory.construct(m_PoseEstimator, m_DriveKinematics, m_gyro, rightLead, leftLead, rightFollower, leftFollower); + + // Init the subsystems + m_limelightSubsystem = new LimelightSubsystem(m_PoseEstimator, m_gyro); + //m_exampleSubsystem = getSubsystem(ExampleSubsystem.class); + + // Init Auto + configureAutos(); + + // Configure the default commands + configureDefaultCommands(); + + // Configure the trigger bindings + configureBindings(); + } + + private void configureAutos() { + // Init Autos (/home/lvuser/deploy/pathplanner/autos) + // AutoCommands.getAutoCommands(m_driveSubsystem); + + // // Init Chooser + // m_autoChooser = AutoBuilder.buildAutoChooser(); // Can make a default by giving a string + m_autoChooser = new SendableChooser(); + + // Named Commands + NamedCommands.registerCommand("RotateTo-180", new RotateToRotation2D( + m_driveSubsystem.get(), + m_PoseEstimator, + Rotation2d.fromDegrees(180), + 1 + ) + ); + + NamedCommands.registerCommand("AimToHub", new AimAtHub( + m_driveSubsystem.get(), + m_PoseEstimator, + 2.0, + isRed() + ) + ); + + // Custom Autos + m_autoChooser.addOption( + "On-Fly Forward 2m", + new DriveForwardFromPos(m_PoseEstimator, 2) + ); + + m_autoChooser.addOption( + "Rotate to 0", + new RotateToRotation2D( + m_driveSubsystem.get(), + m_PoseEstimator, + new Rotation2d(0.0), + 1 + ) + ); + + m_autoChooser.addOption( + "On-The-Fly to (2, 4.837) -90", + new POTFtoPoint( + m_PoseEstimator, + new Pose2d(2, 4.837, Rotation2d.fromDegrees(-90)) + ) + ); + + // Path Planner Auto's + ArrayList autoNames = AutoCommands.getAutoNames(); + for (String autoName : autoNames) { + PathPlannerAuto plannedAuto = new PathPlannerAuto(autoName); + Pose2d startingPose = plannedAuto.getStartingPose(); + + Command rotateToStartRot = new RotateToRotation2D( + m_driveSubsystem.get(), + m_PoseEstimator, + startingPose.getRotation(), + 2.0 + ); + + Command pathfindToStartPose = AutoBuilder.pathfindToPose(startingPose, m_constraints); + Command pathfindThenAuto = Commands.sequence(pathfindToStartPose, rotateToStartRot, plannedAuto); + + m_autoChooser.addOption("[PF] "+autoName, pathfindThenAuto); + } + + // Add to dashboard + SmartDashboard.putData("Auto Chooser", m_autoChooser); + } + + private void configureDefaultCommands() { + if (m_driveSubsystem.isPresent()) + { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + driveSubsystem.initDefaultCommand(m_driverController); + } + } + + /** + * Use this method to define your trigger->command mappings. Triggers can be created via the + * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary + * predicate, or via the named factories in {@link + * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link + * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller + * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight + * joysticks}. + */ + private void configureBindings() { + // SmartDashboard.putBoolean("Example Subsystem", m_exampleSubsystem.isPresent()); + + // if (m_exampleSubsystem.isPresent()) + // { + // ExampleSubsystem exampleSubsystem = m_exampleSubsystem.get(); + + // // Schedule `ExampleCommand` when `exampleCondition` changes to `true` + // new Trigger(exampleSubsystem::exampleCondition) + // .onTrue(new ExampleCommand(exampleSubsystem)); + + // // Schedule `exampleMethodCommand` when the Xbox controller's B button is pressed, + // // cancelling on release. + // m_driverController.b().whileTrue(exampleSubsystem.exampleMethodCommand()); + // } + + m_driverController.y().onTrue(new InstantCommand(() -> CommandScheduler.getInstance().cancelAll())); + + m_driverController.x().onTrue( + new AimAtHub( + m_driveSubsystem.get(), + m_PoseEstimator, + 2.0, + isRed() + ) + ); + } + + // Populate the SmartDashboard on robot init. + public void InitSmartDashboard() { + // Non-Subsystem Specific Stuff + SmartDashboard.putData(CommandScheduler.getInstance()); + SmartDashboard.putData("Field", m_field); + SmartDashboard.putNumber("Target Rotation", 0); + SmartDashboard.putNumber("Target Angle Diff Abs", 0); + SmartDashboard.putNumber("Rotation Speed", 0); + } + + // This function is called every 20mS. + public void UpdateSmartDashboard() { + // Non-subsystem specific stuff + if ((m_iTickCount % DrivetrainConstants.kTicksPerUpdate) == 0) { + Pose2d estimatedPos = m_PoseEstimator.getEstimatedPosition(); + m_field.setRobotPose(estimatedPos); + + SmartDashboard.putNumber("Pose X (Meter)", estimatedPos.getX()); + SmartDashboard.putNumber("Pose Y (Meter)", estimatedPos.getY()); + SmartDashboard.putNumber("Pose Theta (Degrees)", estimatedPos.getRotation().getDegrees()); + + SmartDashboard.putNumber("Limelight robotYaw", m_limelightSubsystem.getRobotYaw()); + } + + // Drive subsystem + if(m_driveSubsystem.isPresent() && (m_iTickCount % DrivetrainConstants.kTicksPerUpdate) == 0) + { + DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + + SmartDashboard.putNumber("Left Speed (MPS)", driveSubsystem.getMotorSpeedMPS(true)); + SmartDashboard.putNumber("Right Speed (MPS)", driveSubsystem.getMotorSpeedMPS(false)); + SmartDashboard.putNumber("Left Speed (RPS)", driveSubsystem.getMotorSpeedRPS(true)); + SmartDashboard.putNumber("Right Speed (RPS)", driveSubsystem.getMotorSpeedRPS(false)); + + SmartDashboard.putNumber("Left Distance (m)", driveSubsystem.getMotorPositionMeters(true)); + SmartDashboard.putNumber("Right Distance (m)", driveSubsystem.getMotorPositionMeters(false)); + + SmartDashboard.putNumber("Gyro Degrees", m_gyro.getRotation2d().getDegrees()); + } + + m_iTickCount++; + } + + // Checks if the robot is on the blue or red alliance. If it cannot get the data it defaults to blue. + public boolean isRed() { + Optional alliance = DriverStation.getAlliance(); + if (alliance.isPresent()) { + return alliance.get() == DriverStation.Alliance.Red; + } else { + System.out.println("Could not fetch alliance! Defaulting to blue!"); + return false; + } + } + + public void disabledInit() {} + + // Move to auto init for competition code. + public void enabledInit() { + LimelightHelpers.SetIMUMode("limelight", 4); + m_gyro.enableLogging(true); + } + + public void teleopInit() { + configureDefaultCommands(); + } + + public void autoInit() { + // This causes issues when auto's do not reset odometry!! + // if (m_driveSubsystem.isPresent()) { + // DriveSubsystem driveSubsystem = m_driveSubsystem.get(); + + // m_gyro.reset(); + // driveSubsystem.resetPose(new Pose2d()); + // } + } + + // Gets called every disabled tick. + public void disabledPeriodic() { + double cameraYaw = m_limelightSubsystem.getRobotYaw(); + + m_gyro.enableLogging(false); + m_gyro.reset(); + m_gyro.setAngleAdjustment(-cameraYaw); + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + return m_autoChooser.getSelected(); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/AimAtHub.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/AimAtHub.java new file mode 100644 index 0000000..4c18c4e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/AimAtHub.java @@ -0,0 +1,76 @@ +package frc.robot.commands; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class AimAtHub extends Command { + private final DriveSubsystem m_driveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final double m_maxSpeed; + private final boolean m_isRed; + + private final Pose2d m_BlueCenter = new Pose2d(4.607, 4.035, new Rotation2d()); + private final Pose2d m_RedCenter = new Pose2d(); + + /** + * Creates a new AimAtHub. + * + * @param driveSubsystem The driveSubsystem for the robot. + * @param poseEstimator The poseEstimator of the robot. + * @param maxSpeed The max speed the robot is allowed to go in the rotation. + * @param isRed Does the robot need to point tword the red or blue goal, default is blue. + */ + public AimAtHub(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, double maxSpeed, boolean isRed) + { + m_driveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_maxSpeed = maxSpeed; + m_isRed = isRed; + + addRequirements(driveSubsystem); + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + Pose2d targetPose; + if (m_isRed) { + targetPose = m_RedCenter; + } else { + targetPose = m_BlueCenter; + } + + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + double dY = targetPose.getY() - currentPose.getY(); + double dX = targetPose.getX() - currentPose.getX(); + + Rotation2d targetRotation = new Rotation2d(Math.atan2(dY, dX)); + RotateToRotation2D aimCommand = new RotateToRotation2D( + m_driveSubsystem, + m_poseEstimator, + targetRotation, + m_maxSpeed + ); + + CommandScheduler.getInstance().schedule(aimCommand); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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 true; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommand.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommand.java new file mode 100644 index 0000000..65eb3bd --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommand.java @@ -0,0 +1,84 @@ +// 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 frc.robot.commands; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.OperatorConstants; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class ArcadeDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double speedInput = -m_driveController.getRightY(); + double turnInput = m_driveController.getLeftX(); + + // Set the deadband + if (Math.abs(speedInput) < OperatorConstants.kDriverControllerDeadband) { + speedInput = 0; + } + if (Math.abs(turnInput) < OperatorConstants.kDriverControllerDeadband) { + turnInput = 0; + } + + double leftSpeed = speedInput + turnInput; + double rightSpeed = speedInput - turnInput; + + // Find the maximum possible value of (throttle + turn) along the vector + // that the joystick is pointing, then desaturate the wheel speeds + double greaterInput = Math.max(Math.abs(speedInput), Math.abs(turnInput)); + double lesserInput = Math.min(Math.abs(speedInput), Math.abs(turnInput)); + if (greaterInput == 0.0) { + m_subsystem.setDifferentialSpeeds( + 0, + 0 + ); + } else { + double saturatedInput = (greaterInput + lesserInput) / greaterInput; + leftSpeed /= saturatedInput; + rightSpeed /= saturatedInput; + + m_subsystem.setDifferentialSpeeds( + leftSpeed * DrivetrainConstants.kMaxVelocityMPS, + rightSpeed * DrivetrainConstants.kMaxVelocityMPS + ); + } + } + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommandNoPID.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommandNoPID.java new file mode 100644 index 0000000..88ee73a --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeDriveCommandNoPID.java @@ -0,0 +1,77 @@ +// 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 frc.robot.commands; +import frc.robot.Constants.OperatorConstants; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; + +/** An example command that uses an example subsystem. */ +public class ArcadeDriveCommandNoPID extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeDriveCommandNoPID(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double speedInput = -m_driveController.getRightY(); + double turnInput = m_driveController.getLeftX(); + + // Set the deadband + if (Math.abs(speedInput) < OperatorConstants.kDriverControllerDeadband) { + speedInput = 0; + } + if (Math.abs(turnInput) < OperatorConstants.kDriverControllerDeadband) { + turnInput = 0; + } + + double leftSpeed = speedInput + turnInput; + double rightSpeed = speedInput - turnInput; + + // Find the maximum possible value of (throttle + turn) along the vector + // that the joystick is pointing, then desaturate the wheel speeds + double greaterInput = Math.max(Math.abs(speedInput), Math.abs(turnInput)); + double lesserInput = Math.min(Math.abs(speedInput), Math.abs(turnInput)); + if (greaterInput == 0.0) { + m_subsystem.setDifferentialSpeedNoPid(0, 0); + } else { + double saturatedInput = (greaterInput + lesserInput) / greaterInput; + leftSpeed /= saturatedInput; + rightSpeed /= saturatedInput; + + m_subsystem.setDifferentialSpeedNoPid(leftSpeed, rightSpeed); + } + } + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeFromDashboard.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeFromDashboard.java new file mode 100644 index 0000000..f35dc4f --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ArcadeFromDashboard.java @@ -0,0 +1,43 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.subsystems.DriveSubsystem; + +public class ArcadeFromDashboard extends Command { + private final DriveSubsystem m_subsystem; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ArcadeFromDashboard(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + double speed = SmartDashboard.getNumber("DB Arcade Set (MPS)", 0); + m_subsystem.setDifferentialSpeeds( speed, speed ); + } + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/Autos.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/Autos.java @@ -0,0 +1,20 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; + +public final class Autos { + /** Example static factory for an autonomous command. */ + public static Command exampleAuto(ExampleSubsystem subsystem) { + return Commands.sequence(subsystem.exampleMethodCommand(), new ExampleCommand(subsystem)); + } + + private Autos() { + throw new UnsupportedOperationException("This is a utility class!"); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentailDriveFromDashboard.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentailDriveFromDashboard.java new file mode 100644 index 0000000..a53bd80 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentailDriveFromDashboard.java @@ -0,0 +1,45 @@ +package frc.robot.commands; + +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.subsystems.DriveSubsystem; + +public class DifferentailDriveFromDashboard extends Command { + private final DriveSubsystem m_subsystem; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public DifferentailDriveFromDashboard(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + double leftSpeed = SmartDashboard.getNumber("DB Left Set (MPS)", 0); + double rightSpeed = SmartDashboard.getNumber("DB Right Set (MPS)", 0); + + m_subsystem.setDifferentialSpeeds( leftSpeed, rightSpeed ); + } + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentialDriveCommand.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentialDriveCommand.java new file mode 100644 index 0000000..16b0322 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DifferentialDriveCommand.java @@ -0,0 +1,66 @@ +// 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 frc.robot.commands; +import frc.robot.subsystems.DriveSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.OperatorConstants; + +/** An example command that uses an example subsystem. */ +public class DifferentialDriveCommand extends Command { + private final DriveSubsystem m_subsystem; + private CommandXboxController m_driveController; + + /** + * Creates a new ArcadeDriveCommand. + * + * @param subsystem The subsystem used by this command. + */ + public DifferentialDriveCommand(DriveSubsystem subsystem, CommandXboxController driveController) { + m_subsystem = subsystem; + m_driveController = driveController; + + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(m_subsystem); + } + + // 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() { + // Note: We negate both axis values so that pushing the joystick forwards + // (which makes the readin more negative) increases the speed and twisting clockwise + // turns the robot clockwise. + double leftSpeed = -m_driveController.getLeftY(); + double rightSpeed = -m_driveController.getRightY(); + + // Set the deadband + if (Math.abs(leftSpeed) < OperatorConstants.kDriverControllerDeadband) { + leftSpeed = 0; + } + if (Math.abs(rightSpeed) < OperatorConstants.kDriverControllerDeadband) { + rightSpeed = 0; + } + + leftSpeed *= DrivetrainConstants.kMaxVelocityMPS; + rightSpeed *= DrivetrainConstants.kMaxVelocityMPS; + + m_subsystem.setDifferentialSpeeds( leftSpeed, rightSpeed ); + } + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DriveForwardFromPos.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DriveForwardFromPos.java new file mode 100644 index 0000000..a17ac62 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/DriveForwardFromPos.java @@ -0,0 +1,98 @@ +package frc.robot.commands; + +import java.util.List; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.GoalEndState; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; +import com.pathplanner.lib.path.Waypoint; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; + +/** An example command that uses an example subsystem. */ +public class DriveForwardFromPos extends Command { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private double m_targetDistance = 0.0; + + /** + * Creates a new DriveForward2mFromPos. + * + * @param subsystem The subsystem used by this command. + */ + public DriveForwardFromPos(DifferentialDrivePoseEstimator poseEstimator, double targetDistance) + { + m_poseEstimator = poseEstimator; + m_targetDistance = targetDistance; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This is taken and modified from the limelight documentation for + // path creation on the fly: https://pathplanner.dev/pplib-create-a-path-on-the-fly.html + + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + Rotation2d currentRotation = currentPose.getRotation(); + + double currentX = currentPose.getX(); + double currentY = currentPose.getY(); + + // If we think of the direction we want to go as of the hypotonuse of a + // right trinagle, we can calculate the needed distance to travel on + // the (x, y) by calculating for them as the legs of the RT. + double xMod = m_targetDistance * currentRotation.getCos(); + double yMod = m_targetDistance * currentRotation.getSin(); + + double newX = currentX + xMod; + double newY = currentY + yMod; + + // Create a list of waypoints from poses. Each pose represents one waypoint. + // The rotation component of the pose should be the direction of travel. Do not use holonomic rotation. + List waypoints = PathPlannerPath.waypointsFromPoses( + new Pose2d(currentX, currentY, currentRotation), + new Pose2d(newX, newY, currentRotation) + ); + + PathConstraints constraints = new PathConstraints( + 1.0, + 1.0, + 2 * Math.PI, + 4 * Math.PI + ); // The constraints for this path. + + // Create the path using the waypoints created above + PathPlannerPath path = new PathPlannerPath( + waypoints, + constraints, + null, // The ideal starting state, this is only relevant for pre-planned paths, so can be null for on-the-fly paths. + new GoalEndState(0.0, currentRotation) // Goal end state. You can set a holonomic rotation here. If using a differential drivetrain, the rotation will have no effect. + ); + + // Prevent the path from being flipped if the coordinates are already correct + path.preventFlipping = true; + + // Create the command version of the path and schedule it + Command followPath = AutoBuilder.followPath(path); + CommandScheduler.getInstance().schedule(followPath); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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() { + // The command automatically ends + return true; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ExampleCommand.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..151624c --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/ExampleCommand.java @@ -0,0 +1,43 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; + +/** An example command that uses an example subsystem. */ +public class ExampleCommand extends Command { + @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField", "unused"}) + private final ExampleSubsystem m_subsystem; + + /** + * Creates a new ExampleCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExampleCommand(ExampleSubsystem subsystem) { + m_subsystem = subsystem; + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(subsystem); + } + + // 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() {} + + // 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/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/POTFtoPoint.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/POTFtoPoint.java new file mode 100644 index 0000000..5990ecc --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/POTFtoPoint.java @@ -0,0 +1,84 @@ +package frc.robot.commands; + +import java.util.List; + +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.path.GoalEndState; +import com.pathplanner.lib.path.PathConstraints; +import com.pathplanner.lib.path.PathPlannerPath; +import com.pathplanner.lib.path.Waypoint; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; + +/** An example command that uses an example subsystem. */ +public class POTFtoPoint extends Command { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private Pose2d m_targetPose; + + /** + * Creates a new POTFtwoPoints. + * + * @param subsystem The subsystem used by this command. + */ + public POTFtoPoint(DifferentialDrivePoseEstimator poseEstimator, Pose2d targetPose) + { + m_poseEstimator = poseEstimator; + m_targetPose = targetPose; + } + + // Called when the command is initially scheduled. + @Override + public void initialize() { + // This is taken and modified from the limelight documentation for + // path creation on the fly: https://pathplanner.dev/pplib-create-a-path-on-the-fly.html + + Pose2d currentPose = m_poseEstimator.getEstimatedPosition(); + + // Create a list of waypoints from poses. Each pose represents one waypoint. + // The rotation component of the pose should be the direction of travel. Do not use holonomic rotation. + List waypoints = PathPlannerPath.waypointsFromPoses( + currentPose, + m_targetPose + ); + + PathConstraints constraints = new PathConstraints( + 1.0, + 1.0, + 2 * Math.PI, + 4 * Math.PI + ); // The constraints for this path. + + // Create the path using the waypoints created above + PathPlannerPath path = new PathPlannerPath( + waypoints, + constraints, + null, // The ideal starting state, this is only relevant for pre-planned paths, so can be null for on-the-fly paths. + new GoalEndState(0.0, m_targetPose.getRotation()) // Goal end state. You can set a holonomic rotation here. If using a differential drivetrain, the rotation will have no effect. + ); + + // Prevent the path from being flipped if the coordinates are already correct + path.preventFlipping = true; + + // Create the command version of the path and schedule it + Command followPath = AutoBuilder.followPath(path); + CommandScheduler.getInstance().schedule(followPath); + } + + // Called every time the scheduler runs while the command is scheduled. + @Override + public void execute() {} + + // 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() { + // The command automatically ends + return true; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/RotateToRotation2D.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/RotateToRotation2D.java new file mode 100644 index 0000000..1b20280 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/commands/RotateToRotation2D.java @@ -0,0 +1,86 @@ +package frc.robot.commands; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +/** An example command that uses an example subsystem. */ +public class RotateToRotation2D extends Command { + private final DriveSubsystem m_driveSubsystem; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final double m_maxSpeed; + private Rotation2d m_targetRotation; + + // Bounds are [0, 1) in interval notation. This effects the steepness of + // the curve that is used to slow the robot as it approches the target. + private double speedExp = 0.98; + + /** + * Creates a new RotateToRotation2D. + * + * @param driveSubsystem The driveSubsystem for the robot. + * @param poseEstimator The poseEstimator of the robot. + * @param targetRotation The rotation you want the robot to go to. + * @param maxSpeed The max speed the robot is allowed to go in the rotation. + */ + public RotateToRotation2D(DriveSubsystem driveSubsystem, DifferentialDrivePoseEstimator poseEstimator, Rotation2d targetRotation, double maxSpeed) + { + m_driveSubsystem = driveSubsystem; + m_poseEstimator = poseEstimator; + m_targetRotation = targetRotation; + m_maxSpeed = maxSpeed; + + SmartDashboard.putNumber("Target Rotation", m_targetRotation.getDegrees()); + + addRequirements(driveSubsystem); + } + + // 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() { + Rotation2d currentRotation = m_poseEstimator.getEstimatedPosition().getRotation(); + Rotation2d changeInRotation = m_targetRotation.minus(currentRotation); + + double targetDiff = Math.abs(changeInRotation.getDegrees()); + + double speed = ((Math.pow(speedExp, targetDiff)-1)/(Math.pow(speedExp, 180)-1)); + speed *= m_maxSpeed; + speed = Math.max(speed, 0.3); + SmartDashboard.putNumber("Rotation Speed", speed); + + if (changeInRotation.getDegrees() <= 0) { + m_driveSubsystem.setDifferentialSpeeds(speed, -speed); + } else { + m_driveSubsystem.setDifferentialSpeeds(-speed, speed); + } + } + + // Called once the command ends or is interrupted. + @Override + public void end(boolean interrupted) { + System.out.println("Ended Rotation fix!"); + m_driveSubsystem.setDifferentialSpeeds(0, 0); + } + + // Returns true when the command should end. + @Override + public boolean isFinished() { + Rotation2d currentRotation = m_poseEstimator.getEstimatedPosition().getRotation(); + Rotation2d diff = m_targetRotation.minus(currentRotation); + + double currentDiffAbs = Math.abs(diff.getDegrees()); + SmartDashboard.putNumber("Target Angle Diff Abs", currentDiffAbs); + + if (currentDiffAbs <= 0.5) { + return true; + } else { + return false; + } + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java new file mode 100644 index 0000000..afc5b84 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/DriveSubsystemFactory.java @@ -0,0 +1,33 @@ +package frc.robot.factorys; + +import java.util.Optional; +import com.ctre.phoenix6.hardware.TalonFX; +import com.studica.frc.AHRS; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import frc.robot.subsystems.DriveSubsystem; + +public class DriveSubsystemFactory { + public DriveSubsystemFactory() { + } + + public Optional construct(DifferentialDrivePoseEstimator poseEstimator, + DifferentialDriveKinematics kinematics, + AHRS gyro, Optional rightLead, Optional leftLead, Optional rightFollower, + Optional leftFollower) { + + if (rightLead.isEmpty() || leftLead.isEmpty()) { + return Optional.empty(); + } + + DriveSubsystem driveSubsystem = new DriveSubsystem(poseEstimator, kinematics, gyro, rightLead.get(), + leftLead.get()); + + if (rightFollower.isPresent() && leftFollower.isPresent()) { + driveSubsystem.setFollowers(rightFollower.get(), leftFollower.get()); + } + + return Optional.of(driveSubsystem); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/TalonFXFactory.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/TalonFXFactory.java new file mode 100644 index 0000000..e50c14e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/factorys/TalonFXFactory.java @@ -0,0 +1,19 @@ +package frc.robot.factorys; + +import java.util.Optional; + +import com.ctre.phoenix6.hardware.TalonFX; + +public class TalonFXFactory { + public TalonFXFactory() {} + + public Optional construct(int CANID) { + TalonFX talon = new TalonFX(CANID); + + if (talon.isConnected()) { + return Optional.of(talon); + } else { + return Optional.empty(); + } + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/DriveSubsystem.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/DriveSubsystem.java new file mode 100644 index 0000000..67ac57b --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/DriveSubsystem.java @@ -0,0 +1,325 @@ +// 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. + +// https://github.com/CrossTheRoadElec/Phoenix6-Examples/blob/main/java/VelocityClosedLoop/src/main/java/frc/robot/Robot.java +// This is a link to do speed control on the kraken motors. We need something like this. +package frc.robot.subsystems; +import static edu.wpi.first.units.Units.Volts; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.Follower; +import com.ctre.phoenix6.controls.MotionMagicVelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.signals.InvertedValue; +import com.ctre.phoenix6.signals.MotorAlignmentValue; +import com.ctre.phoenix6.signals.NeutralModeValue; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.controllers.PPLTVController; +import com.studica.frc.AHRS; + +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.DifferentialDriveKinematics; +import edu.wpi.first.math.kinematics.DifferentialDriveWheelSpeeds; +import edu.wpi.first.util.sendable.SendableRegistry; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import frc.robot.Constants.AutoConstants; +import frc.robot.Constants.DrivetrainConstants; +import frc.robot.commands.ArcadeDriveCommand; +import frc.robot.commands.ArcadeDriveCommandNoPID; +import frc.robot.commands.ArcadeFromDashboard; +import frc.robot.commands.DifferentialDriveCommand; + +public class DriveSubsystem extends SubsystemBase { + private TalonFX m_rightMotor; + private TalonFX m_optionalRightMotor; + + private TalonFX m_leftMotor; + private TalonFX m_optionalLeftMotor; + + /* Start at velocity 0, use slot 0 */ + private final MotionMagicVelocityVoltage m_leftVelocityVoltage = new MotionMagicVelocityVoltage(0);//.withSlot(0); + private final MotionMagicVelocityVoltage m_rightVelocityVoltage = new MotionMagicVelocityVoltage(0);//.withSlot(0); + + private final DifferentialDriveKinematics m_kinematics; + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final AHRS m_gyro; + + private final SendableChooser> m_driveChooser = new SendableChooser<>(); + + /** Creates a new DriveSubsystem. */ + public DriveSubsystem(DifferentialDrivePoseEstimator poseEstimator, DifferentialDriveKinematics kinematics, AHRS gyro, TalonFX rightMotor, TalonFX leftMotor) { + m_poseEstimator = poseEstimator; + m_kinematics = kinematics; + m_gyro = gyro; + m_rightMotor = rightMotor; + m_leftMotor = leftMotor; + + // Right Motor + setConfig(m_rightMotor, InvertedValue.CounterClockwise_Positive); + SendableRegistry.setName(m_rightMotor, "DriveSubsystem", "rightMotor"); + + // Left Motor + setConfig(m_leftMotor, InvertedValue.Clockwise_Positive); + SendableRegistry.setName(m_leftMotor, "DriveSubsystem", "leftMotor"); + + // Zeroing the encoders + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + + // Init Autobuilder + AutoBuilder.configure( + this::getPose, + this::resetPose, + this::getRobotRelativeSpeeds, + (speeds, feedforwards) -> driveRobotRelative(speeds), + new PPLTVController(0.02), + AutoConstants.kRobotConfig, + () -> { + // Boolean supplier that controls when the path will be mirrored for the red alliance + // This will flip the path being followed to the red side of the field. + // THE ORIGIN WILL REMAIN ON THE BLUE SIDE + + var alliance = DriverStation.getAlliance(); + if (alliance.isPresent()) { + return alliance.get() == DriverStation.Alliance.Red; + } + return false; + }, + this + ); + + // Gyro setup + m_gyro.reset(); + + // Drive Chooser Init + m_driveChooser.setDefaultOption("PID Arcade", ArcadeDriveCommand.class); + m_driveChooser.addOption("PID Differential", DifferentialDriveCommand.class); + m_driveChooser.addOption("NO-PID Arcade", ArcadeDriveCommandNoPID.class); + m_driveChooser.addOption("Dashboard PID Arcade", ArcadeFromDashboard.class); + SmartDashboard.putData("Drive Commands", m_driveChooser); + + // Init Smartdashboard + SmartDashboard.putNumber("DB Left Set (MPS)", 0); + SmartDashboard.putNumber("DB Right Set (MPS)", 0); + SmartDashboard.putNumber("DB Arcade Set (MPS)", 0); + + SmartDashboard.putNumber("Left Target (MPS)", 0); + SmartDashboard.putNumber("Right Target (MPS)", 0); + SmartDashboard.putNumber("Left Speed (MPS)", getMotorSpeedMPS(true)); + SmartDashboard.putNumber("Right Speed (MPS)", getMotorSpeedMPS(false)); + SmartDashboard.putNumber("Left Speed (RPS)", getMotorSpeedRPS(true)); + SmartDashboard.putNumber("Right Speed (RPS)", getMotorSpeedRPS(false)); + + SmartDashboard.putNumber("Gyro Degrees", m_gyro.getRotation2d().getDegrees()); + } + + private void setConfig(TalonFX motor, InvertedValue Inverted) { + TalonFXConfiguration motorConfig = new TalonFXConfiguration(); + motorConfig.Slot0.kS = DrivetrainConstants.kS; + motorConfig.Slot0.kV = DrivetrainConstants.kV; + motorConfig.Slot0.kA = DrivetrainConstants.kA_linear; + motorConfig.Slot0.kP = DrivetrainConstants.kP; + motorConfig.Slot0.kI = DrivetrainConstants.kI; // No output for integrated error + motorConfig.Slot0.kD = DrivetrainConstants.kD; // A velocity of 1 rps results in 0.1 V output + + motorConfig.MotorOutput.NeutralMode = NeutralModeValue.Brake; + motorConfig.MotorOutput.Inverted = Inverted; + + motorConfig.Voltage.withPeakForwardVoltage(Volts.of(DrivetrainConstants.kPeakVoltage)) + .withPeakReverseVoltage(Volts.of(-DrivetrainConstants.kPeakVoltage)); + + var motionMagicConfigs = motorConfig.MotionMagic; + motionMagicConfigs.MotionMagicAcceleration = DrivetrainConstants.kMotionMagicAcceleration; + motionMagicConfigs.MotionMagicJerk = DrivetrainConstants.kMotionMagicJerk; + + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = motor.getConfigurator().apply(motorConfig); + System.out.println("config attempt #" + (i+1) + ": " + status.toString()); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs, error code: " + status.toString()); + } + } + + public void setFollowers(TalonFX optionalRight, TalonFX optionalLeft) { + // Right Motor + m_optionalRightMotor = optionalRight; + + setConfig(m_optionalRightMotor, InvertedValue.CounterClockwise_Positive); + m_optionalRightMotor.setControl( + new Follower(m_rightMotor.getDeviceID(), MotorAlignmentValue.Aligned) + ); + + // Left Motor + m_optionalLeftMotor = optionalLeft; + + setConfig(m_optionalLeftMotor, InvertedValue.Clockwise_Positive); + m_optionalLeftMotor.setControl( + new Follower(m_leftMotor.getDeviceID(), MotorAlignmentValue.Aligned) + ); + } + + // This takes the current class selected in the drive chooser and attempts to initilize a command object from it & run it. + // The commands must have a constructor with a DriveSubsystem and CommandXboxController as args. + public void initDefaultCommand(CommandXboxController Controller) + { + Class driveClass = m_driveChooser.getSelected(); + + try { + Object driveCommand = driveClass.getDeclaredConstructor(DriveSubsystem.class, CommandXboxController.class).newInstance(this, Controller); + setDefaultCommand((Command) driveCommand); + + System.out.println(String.format("Loaded drive command \"%s\"!", driveClass.getName())); + } catch (Exception e) { + DriverStation.reportWarning( + String.format( + "Failed to load drive command \"%s\"!", driveClass.getName()), + false + ); + + e.printStackTrace(); + } + + // setDefaultCommand(new ArcadeDriveCommand(this, Controller)); + } + + // TODO: Work on a naming scheme for conversion functions + + // This converts rotations of the motor shaft to meters travled. + public double ConvertRotationsToMeters(double rotations) { + return rotations * DrivetrainConstants.kDrivetrainGearRatio * DrivetrainConstants.kWheelCircumference; + } + + // This converts meters travled to rotations of the motor shaft. + // This is the inverse function of the one above, calculated it myself. - Owen + public double ConvertMetersToRotations(double meters) { + return (meters / DrivetrainConstants.kDrivetrainGearRatio) / DrivetrainConstants.kWheelCircumference; + } + + // This sets the differential speeds based on a desired MPS speed. + public void setDifferentialSpeeds(double leftSpeedMPS, double rightSpeedMPS) + { + SmartDashboard.putNumber("Left Target (MPS)", leftSpeedMPS); + SmartDashboard.putNumber("Right Target (MPS)", rightSpeedMPS); + + double leftSpeedRPS = ConvertMetersToRotations(leftSpeedMPS); + double rightSpeedRPS = ConvertMetersToRotations(rightSpeedMPS); + + SmartDashboard.putNumber("Left Target (RPS)", leftSpeedRPS); + SmartDashboard.putNumber("Right Target (RPS)", rightSpeedRPS); + + m_leftMotor.setControl(m_leftVelocityVoltage.withVelocity(leftSpeedRPS)); + m_rightMotor.setControl(m_rightVelocityVoltage.withVelocity(rightSpeedRPS)); + } + + // pass in -1 to 1 for the motor speed with no closed loop control. + public void setDifferentialSpeedNoPid(double left, double right) { + m_leftMotor.set(left); + m_rightMotor.set(right); + } + + // Wrapping robot position inside of getposition + public Pose2d getPose(){ + return m_poseEstimator.getEstimatedPosition(); + } + + // Resets the drivetrains pose estimator to zero. + public void resetPose(Pose2d newPose){ + m_leftMotor.setPosition(0); + m_rightMotor.setPosition(0); + + m_poseEstimator.resetPosition( + m_gyro.getRotation2d(), + 0, 0, + newPose + ); + } + + public double getMotorSpeedRPS(boolean bLeft) + { + double RPS; + if (bLeft) + { + RPS = m_leftMotor.getVelocity().getValueAsDouble(); + } + else + { + RPS = m_rightMotor.getVelocity().getValueAsDouble(); + } + return RPS; + } + + public double getMotorSpeedMPS(boolean bLeft) + { + double RPS = getMotorSpeedRPS(bLeft); + double MPS = ConvertRotationsToMeters(RPS); + return MPS; + } + + // Gets the distance traveled by the motor in meters + public double getMotorPositionMeters(boolean bLeft) { + double posRotations; + if (bLeft == true) { + posRotations = m_leftMotor.getPosition().getValueAsDouble(); + } else { + posRotations = m_rightMotor.getPosition().getValueAsDouble(); + } + + double posMeters = ConvertRotationsToMeters(posRotations); + return posMeters; + } + + // Returns a robot relative ChassisSpeeds object based on the average linear velocity + // in meters per second and average angular velocity in radians per second. + // Currently we are assuming that there is no scale for the motors, we cannot find anywhere to set the scale. + public ChassisSpeeds getRobotRelativeSpeeds() + { + // Linear velocity in meters per second + double leftMPS = getMotorSpeedMPS(true); + double rightMPS = getMotorSpeedMPS(false); + + // Make wheelSpeeds object from MPS & converts it to chasis speeds + DifferentialDriveWheelSpeeds wheelSpeeds = new DifferentialDriveWheelSpeeds(leftMPS, rightMPS); + return m_kinematics.toChassisSpeeds(wheelSpeeds); + } + + // Gives the drivetrain a new drive command based on a robot relative + // chassis speed object. + public void driveRobotRelative(ChassisSpeeds relativeChassisSpeed){ + DifferentialDriveWheelSpeeds wheelSpeeds = m_kinematics.toWheelSpeeds(relativeChassisSpeed); + + setDifferentialSpeeds(wheelSpeeds.leftMetersPerSecond, wheelSpeeds.rightMetersPerSecond); + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + double lPositionMeters = getMotorPositionMeters(true); + double rPositionMeters = getMotorPositionMeters(false); + Rotation2d yaw = m_gyro.getRotation2d(); + //m_gyro.getAngle() + + m_poseEstimator.update( + yaw, + lPositionMeters, + rPositionMeters + ); + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..6b375da --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/ExampleSubsystem.java @@ -0,0 +1,47 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class ExampleSubsystem extends SubsystemBase { + /** Creates a new ExampleSubsystem. */ + public ExampleSubsystem() {} + + /** + * Example command factory method. + * + * @return a command + */ + public Command exampleMethodCommand() { + // Inline construction of command goes here. + // Subsystem::RunOnce implicitly requires `this` subsystem. + return runOnce( + () -> { + /* one-time action goes here */ + }); + } + + /** + * An example method querying a boolean state of the subsystem (for example, a digital sensor). + * + * @return value of some boolean subsystem state, such as a digital sensor. + */ + public boolean exampleCondition() { + // Query some boolean state, such as a digital sensor. + return false; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/LimelightSubsystem.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/LimelightSubsystem.java new file mode 100644 index 0000000..4e21387 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/subsystems/LimelightSubsystem.java @@ -0,0 +1,110 @@ +// 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 frc.robot.subsystems; +import java.util.Arrays; + +import org.ejml.simple.SimpleMatrix; + +import com.studica.frc.AHRS; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.Vector; +import edu.wpi.first.math.estimator.DifferentialDrivePoseEstimator; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.LimelightHelpers; +import frc.robot.LimelightHelpers.IMUData; +import frc.robot.utils.LimitedQueue; + +public class LimelightSubsystem extends SubsystemBase { + private final DifferentialDrivePoseEstimator m_poseEstimator; + private final AHRS m_gyro; + + private final LimitedQueue m_YawQue = new LimitedQueue(5); + + /** Creates a new PositionSubsystem. */ + public LimelightSubsystem(DifferentialDrivePoseEstimator poseEstimator, AHRS gyro) { + m_poseEstimator = poseEstimator; + m_gyro = gyro; + + m_gyro.enableLogging(false); + } + + public double getRobotYaw() { + Double[] doubleObjects = m_YawQue.toArray(new Double[0]); + int queSize = doubleObjects.length; + + double[] primitiveDoubles = new double[queSize]; + for (int i = 0; i < queSize; i++) { + primitiveDoubles[i] = doubleObjects[i].doubleValue(); + } + + SimpleMatrix samplesMatrix = new SimpleMatrix(primitiveDoubles); + + double[] weightsArray = new double[queSize]; + Arrays.fill(weightsArray, 1.0); + + SimpleMatrix weightsMatrix = new SimpleMatrix(1, queSize, true, weightsArray); + + double avrg = samplesMatrix.dot(weightsMatrix)/weightsMatrix.elementSum(); + return avrg; + } + + // This is copy and pasted from limelight's documentation for testing. + private void updateOdometry() { + boolean doRejectUpdate = false; + + LimelightHelpers.SetRobotOrientation("limelight", m_poseEstimator.getEstimatedPosition().getRotation().getDegrees(), 0, 0, 0, 0, 0); + LimelightHelpers.PoseEstimate mt2 = LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2("limelight"); + if (mt2 == null) { + return; + } + + // if our angular velocity is greater than 720 degrees per second, ignore vision updates + if(Math.abs(m_gyro.getRate()) > 720) + { + doRejectUpdate = true; + } + + if (Math.abs(m_gyro.getPitch()) > 5) { + doRejectUpdate = true; + } + + if(mt2.tagCount == 0) + { + doRejectUpdate = true; + } + + if(!doRejectUpdate) + { + // A Static Standard Deviation, in the form of [x, y, theta]ᵀ in meters and radians. + Vector errorVec; + + // When the robot is disabled, trust the gyro from the limelight fully. + // Otherwise, in all othr modes, trust the gyro yaw. + if (DriverStation.isDisabled()) { + errorVec = VecBuilder.fill(.7,.7,0); + } else { + errorVec = VecBuilder.fill(.7,.7,9999999); + } + m_poseEstimator.setVisionMeasurementStdDevs(errorVec); + + m_poseEstimator.addVisionMeasurement( + mt2.pose, + mt2.timestampSeconds + ); + } + } + + @Override + public void periodic() { + // This method will be called once per scheduler + IMUData limelightData = LimelightHelpers.getIMUData("limelight"); + m_YawQue.add(limelightData.robotYaw); + + updateOdometry(); + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/AutoCommands.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/AutoCommands.java new file mode 100644 index 0000000..5475bf5 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/AutoCommands.java @@ -0,0 +1,59 @@ +package frc.robot.utils; + +import java.io.File; +import edu.wpi.first.wpilibj.Filesystem; +import edu.wpi.first.wpilibj2.command.Command; +import frc.robot.subsystems.DriveSubsystem; + +import java.util.ArrayList; +import java.util.Optional; + +import com.pathplanner.lib.commands.PathPlannerAuto; + +// This goes to "/home/lvuser/deploy/pathplanner/autos" and grabs the names of all .auto files +// We are asuming that the PathPlannerAuto(string) is the file name of the auto +// We are asuming that the files are always in the "/home/lvuser/deploy/pathplanner/autos" +// We found this information at https://github.com/mjansen4857/pathplanner/wiki/PathPlannerLib:-Java-Usage +public class AutoCommands { + public static ArrayList getAutoNames() { + // Creating an ArrayList so I can fill it with auto file names w/o the extention. + ArrayList autoNames = new ArrayList(); + + // Getting the folder and files. + String deployDirectoryPath = Filesystem.getDeployDirectory().getPath(); + File deployDirectory = new File(deployDirectoryPath+"/pathplanner/autos"); + File[] listOfFiles = deployDirectory.listFiles(); + + // Checking if the files exist + if (listOfFiles != null) { + for (File file : listOfFiles) { + String fullName = file.getName(); // Returns [file_name].[extention] + if (file.isFile()) { + String[] splitName = fullName.split("\\."); // Splitting the file name into seprate parts. + + // If its an auto file then add it to the list. + if (splitName.length > 1 && "auto".equals(splitName[1])) { + autoNames.add(splitName[0]); + System.out.println("FOUND AUTO: "+splitName[0]); + } + } + } + } + + // Returning the new array + return autoNames; + } + + public static ArrayList getAutoCommands(Optional driveSubsystem) { + ArrayList autoCommands = new ArrayList(); + + if (driveSubsystem.isPresent()) { + ArrayList autoNames = getAutoNames(); + for (String autoName : autoNames) { + autoCommands.add(new PathPlannerAuto(autoName)); + } + } + + return autoCommands; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/LimitedQueue.java b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/LimitedQueue.java new file mode 100644 index 0000000..cc0a705 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/src/main/java/frc/robot/utils/LimitedQueue.java @@ -0,0 +1,19 @@ +package frc.robot.utils; + +import java.util.LinkedList; + +// An extention of LinkedList that limits the max number of elements when using add and remove. +public class LimitedQueue extends LinkedList { + private int limit; + + public LimitedQueue(int limit) { + this.limit = limit; + } + + @Override + public boolean add(E o) { + super.add(o); + while (size() > limit) { super.remove(); } + return true; + } +} diff --git a/prototype_projects/Experimental_Drivetrain_2026/vendordeps/PathplannerLib-2026.1.2.json b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/PathplannerLib-2026.1.2.json new file mode 100644 index 0000000..f72fa41 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/PathplannerLib-2026.1.2.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2026.1.2.json", + "name": "PathplannerLib", + "version": "2026.1.2", + "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", + "frcYear": "2026", + "mavenUrls": [ + "https://3015rangerrobotics.github.io/pathplannerlib/repo" + ], + "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", + "javaDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-java", + "version": "2026.1.2" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2026.1.2", + "libName": "PathplannerLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal", + "linuxathena", + "linuxarm32", + "linuxarm64" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Phoenix6-26.1.0.json b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Phoenix6-26.1.0.json new file mode 100644 index 0000000..dc5dc62 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Phoenix6-26.1.0.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-26.1.0.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.0", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/vendordeps/REVLib.json b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/REVLib.json new file mode 100644 index 0000000..4f96af8 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/REVLib.json @@ -0,0 +1,133 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2026.0.0", + "frcYear": "2026", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2026.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2026.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2026.0.0", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.0", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.0", + "libName": "BackendDriver", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.0", + "libName": "REVLibWpi", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Studica.json b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Studica.json new file mode 100644 index 0000000..b51bf58 --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/Studica.json @@ -0,0 +1,71 @@ +{ + "fileName": "Studica.json", + "name": "Studica", + "version": "2026.0.0", + "frcYear": "2026", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "mavenUrls": [ + "https://dev.studica.com/maven/release/2026/" + ], + "jsonUrl": "https://dev.studica.com/maven/release/2026/json/Studica-2026.0.0.json", + "javaDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-java", + "version": "2026.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-driver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.studica.frc", + "artifactId": "Studica-cpp", + "version": "2026.0.0", + "libName": "Studica", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.studica.frc", + "artifactId": "Studica-driver", + "version": "2026.0.0", + "libName": "StudicaDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/Experimental_Drivetrain_2026/vendordeps/WPILibNewCommands.json b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/Experimental_Drivetrain_2026/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/FuelTransport/.gitignore b/prototype_projects/FuelTransport/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/FuelTransport/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/FuelTransport/.vscode/launch.json b/prototype_projects/FuelTransport/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/FuelTransport/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/FuelTransport/.vscode/settings.json b/prototype_projects/FuelTransport/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/FuelTransport/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/FuelTransport/.wpilib/wpilib_preferences.json b/prototype_projects/FuelTransport/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..eadec71 --- /dev/null +++ b/prototype_projects/FuelTransport/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 7762 +} diff --git a/prototype_projects/FuelTransport/WPILib-License.md b/prototype_projects/FuelTransport/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/FuelTransport/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/FuelTransport/build.gradle b/prototype_projects/FuelTransport/build.gradle new file mode 100644 index 0000000..e9b0020 --- /dev/null +++ b/prototype_projects/FuelTransport/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = true + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/FuelTransport/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/FuelTransport/gradlew b/prototype_projects/FuelTransport/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/FuelTransport/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/FuelTransport/gradlew.bat b/prototype_projects/FuelTransport/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/FuelTransport/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/FuelTransport/settings.gradle b/prototype_projects/FuelTransport/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/FuelTransport/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/FuelTransport/src/main/deploy/example.txt b/prototype_projects/FuelTransport/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/FuelTransport/src/main/java/frc/robot/Main.java b/prototype_projects/FuelTransport/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/FuelTransport/src/main/java/frc/robot/Mechanisms.java b/prototype_projects/FuelTransport/src/main/java/frc/robot/Mechanisms.java new file mode 100644 index 0000000..572e396 --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/java/frc/robot/Mechanisms.java @@ -0,0 +1,58 @@ +package frc.robot; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.StatusSignal; + +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d; +import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj.util.Color; +import edu.wpi.first.wpilibj.util.Color8Bit; + +/** + * Class to keep all the mechanism-specific objects together and out of the main example + */ +public class Mechanisms { + double HEIGHT = 1; // Controls the height of the mech2d SmartDashboard + double WIDTH = 1; // Controls the height of the mech2d SmartDashboard + + Mechanism2d mech = new Mechanism2d(WIDTH, HEIGHT); + // Velocity + MechanismLigament2d VelocityMech = mech. + getRoot("velocityLineReferencePosition", 0.75, 0.5). + append(new MechanismLigament2d("velocityLine", 1,90, 6, new Color8Bit(Color.kAliceBlue))); + + MechanismLigament2d midline = mech. + getRoot("midline", 0.7, 0.5). + append(new MechanismLigament2d("midline", 0.1, 0, 3, new Color8Bit(Color.kCyan))); + + //Position + MechanismLigament2d arm = mech. + getRoot("pivotPoint", 0.25, 0.5). + append(new MechanismLigament2d("arm", .2, 0, 0, new Color8Bit(Color.kAliceBlue))); + + MechanismLigament2d side1 = arm.append(new MechanismLigament2d("side1", 0.15307, 112.5, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side2 = side1.append(new MechanismLigament2d("side2", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side3 = side2.append(new MechanismLigament2d("side3", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side4 = side3.append(new MechanismLigament2d("side4", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side5 = side4.append(new MechanismLigament2d("side5", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side6 = side5.append(new MechanismLigament2d("side6", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side7 = side6.append(new MechanismLigament2d("side7", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side8 = side7.append(new MechanismLigament2d("side8", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + + /** + * Runs the mech2d widget in GUI. + * + * This utilizes GUI to simulate and display a TalonFX and exists to allow users to test and understand + * features of our products in simulation using our examples out of the box. Users may modify to have a + * display interface that they find more intuitive or visually appealing. + */ + public void update(StatusSignal position, StatusSignal velocity) { + VelocityMech.setLength(velocity.getValue().in(RotationsPerSecond)/120); + arm.setAngle(position.getValue().in(Rotations) * 360); // Divide by 120 to scale motion to fit in the window + SmartDashboard.putData("mech2d", mech); // Creates mech2d in SmartDashboard + } +} diff --git a/prototype_projects/FuelTransport/src/main/java/frc/robot/Robot.java b/prototype_projects/FuelTransport/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..f36d3ca --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/java/frc/robot/Robot.java @@ -0,0 +1,100 @@ +// 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 frc.robot; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.VelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; + +/** + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private final TalonFX m_fx = new TalonFX(30); + + /* Be able to switch which control request to use based on a button press */ + /* Start at velocity 0, use slot 0 */ + private final VelocityVoltage m_velocityVoltage = new VelocityVoltage(0).withSlot(0); + private final XboxController m_joystick = new XboxController(0); + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + public Robot() { + TalonFXConfiguration configs = new TalonFXConfiguration(); + + /* Voltage-based velocity requires a velocity feed forward to account for the back-emf of the motor */ + configs.Slot0.kS = 0.1; // To account for friction, add 0.1 V of static feedforward + configs.Slot0.kV = 0.12; // Kraken X60 is a 500 kV motor, 500 rpm per V = 8.333 rps per V, 1/8.33 = 0.12 volts / rotation per second + configs.Slot0.kP = 0.11; // An error of 1 rotation per second results in 0.11 V output + configs.Slot0.kI = 0; // No output for integrated error + configs.Slot0.kD = 0; // No output for error derivative + // Peak output of 8 volts + configs.Voltage.withPeakForwardVoltage(Volts.of(8)) + .withPeakReverseVoltage(Volts.of(-8)); + + /* Retry config apply up to 5 times, report if failure */ + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = m_fx.getConfigurator().apply(configs); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs, error code: " + status.toString()); + } + + SmartDashboard.putNumber("desired RPS", 0); + SmartDashboard.putNumber("current RPS", m_fx.getVelocity().getValueAsDouble()); + } + + @Override + public void robotPeriodic() { + } + + @Override + public void autonomousInit() {} + + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() {} + + @Override + public void teleopPeriodic() { + double joyValue = m_joystick.getLeftY(); + if (Math.abs(joyValue) < 0.1) joyValue = 0; + + double desiredRotationsPerSecond = joyValue * 125; // Go for plus/minus 50 rotations per second + + /* Use velocity voltage */ + m_fx.setControl(m_velocityVoltage.withVelocity(desiredRotationsPerSecond)); + + SmartDashboard.putNumber("desired RPS", desiredRotationsPerSecond); + SmartDashboard.putNumber("current RPS", m_fx.getVelocity().getValueAsDouble()); + } + + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + @Override + public void testInit() {} + + @Override + public void testPeriodic() {} +} diff --git a/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/PhysicsSim.java b/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/PhysicsSim.java new file mode 100644 index 0000000..6000bca --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/PhysicsSim.java @@ -0,0 +1,81 @@ +package frc.robot.sim; + +import java.util.ArrayList; + +import com.ctre.phoenix6.Utils; +import com.ctre.phoenix6.hardware.TalonFX; + +/** + * Manages physics simulation for CTRE products. + */ +public class PhysicsSim { + private static final PhysicsSim sim = new PhysicsSim(); + + /** + * Gets the robot simulator instance. + */ + public static PhysicsSim getInstance() { + return sim; + } + + /** + * Adds a TalonFX controller to the simulator. + * + * @param talonFX + * The TalonFX device + * @param rotorInertia + * Rotational Inertia of the mechanism at the rotor + */ + public void addTalonFX(TalonFX talonFX, final double rotorInertia) { + if (talonFX != null) { + TalonFXSimProfile simTalonFX = new TalonFXSimProfile(talonFX, rotorInertia); + _simProfiles.add(simTalonFX); + } + } + + /** + * Runs the simulator: + * - enable the robot + * - simulate sensors + */ + public void run() { + // Simulate devices + for (SimProfile simProfile : _simProfiles) { + simProfile.run(); + } + } + + private final ArrayList _simProfiles = new ArrayList(); + + /** + * Holds information about a simulated device. + */ + static class SimProfile { + private double _lastTime; + private boolean _running = false; + + /** + * Runs the simulation profile. + * Implemented by device-specific profiles. + */ + public void run() { + } + + /** + * Returns the time since last call, in seconds. + */ + protected double getPeriod() { + // set the start time if not yet running + if (!_running) { + _lastTime = Utils.getCurrentTimeSeconds(); + _running = true; + } + + double now = Utils.getCurrentTimeSeconds(); + final double period = now - _lastTime; + _lastTime = now; + + return period; + } + } +} \ No newline at end of file diff --git a/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/TalonFXSimProfile.java b/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/TalonFXSimProfile.java new file mode 100644 index 0000000..0dc9756 --- /dev/null +++ b/prototype_projects/FuelTransport/src/main/java/frc/robot/sim/TalonFXSimProfile.java @@ -0,0 +1,57 @@ +package frc.robot.sim; + +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.sim.TalonFXSimState; + +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import frc.robot.sim.PhysicsSim.SimProfile; + +/** + * Holds information about a simulated TalonFX. + */ +class TalonFXSimProfile extends SimProfile { + private static final double kMotorResistance = 0.002; // Assume 2mOhm resistance for voltage drop calculation + private final TalonFXSimState _talonFXSim; + private final DCMotorSim _motorSim; + + /** + * Creates a new simulation profile for a TalonFX device. + * + * @param talonFX + * The TalonFX device + * @param rotorInertia + * Rotational Inertia of the mechanism at the rotor + */ + public TalonFXSimProfile(final TalonFX talonFX, final double rotorInertia) { + var gearbox = DCMotor.getKrakenX60Foc(1); + this._motorSim = new DCMotorSim(LinearSystemId.createDCMotorSystem(gearbox, rotorInertia, 1.0), gearbox); + this._talonFXSim = talonFX.getSimState(); + } + + /** + * Runs the simulation profile. + * + * This uses very rudimentary physics simulation and exists to allow users to + * test features of our products in simulation using our examples out of the + * box. Users may modify this to utilize more accurate physics simulation. + */ + public void run() { + /// DEVICE SPEED SIMULATION + + _motorSim.setInputVoltage(_talonFXSim.getMotorVoltage()); + + _motorSim.update(getPeriod()); + + /// SET SIM PHYSICS INPUTS + final double position_rot = _motorSim.getAngularPositionRotations(); + final double velocity_rps = Units.radiansToRotations(_motorSim.getAngularVelocityRadPerSec()); + + _talonFXSim.setRawRotorPosition(position_rot); + _talonFXSim.setRotorVelocity(velocity_rps); + + _talonFXSim.setSupplyVoltage(12 - _talonFXSim.getSupplyCurrent() * kMotorResistance); + } +} \ No newline at end of file diff --git a/prototype_projects/FuelTransport/vendordeps/Phoenix6-frc2026-latest.json b/prototype_projects/FuelTransport/vendordeps/Phoenix6-frc2026-latest.json new file mode 100644 index 0000000..8f6e30f --- /dev/null +++ b/prototype_projects/FuelTransport/vendordeps/Phoenix6-frc2026-latest.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-frc2026-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.0", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/FuelTransport/vendordeps/WPILibNewCommands.json b/prototype_projects/FuelTransport/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/FuelTransport/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/VelocityClosedLoop/src/main/java/frc/robot/Robot.java b/prototype_projects/VelocityClosedLoop/src/main/java/frc/robot/Robot.java index fbc7ae3..eb8971a 100644 --- a/prototype_projects/VelocityClosedLoop/src/main/java/frc/robot/Robot.java +++ b/prototype_projects/VelocityClosedLoop/src/main/java/frc/robot/Robot.java @@ -27,9 +27,8 @@ * project. */ public class Robot extends TimedRobot { - private final CANBus canbus = new CANBus("canivore"); - private final TalonFX m_fx = new TalonFX(0, canbus); - private final TalonFX m_fllr = new TalonFX(1, canbus); + private final TalonFX m_fx = new TalonFX(10); + private final TalonFX m_fllr = new TalonFX(20); /* Be able to switch which control request to use based on a button press */ /* Start at velocity 0, use slot 0 */ @@ -51,11 +50,11 @@ public Robot() { TalonFXConfiguration configs = new TalonFXConfiguration(); /* Voltage-based velocity requires a velocity feed forward to account for the back-emf of the motor */ - configs.Slot0.kS = 0.1; // To account for friction, add 0.1 V of static feedforward - configs.Slot0.kV = 0.12; // Kraken X60 is a 500 kV motor, 500 rpm per V = 8.333 rps per V, 1/8.33 = 0.12 volts / rotation per second - configs.Slot0.kP = 0.11; // An error of 1 rotation per second results in 0.11 V output - configs.Slot0.kI = 0; // No output for integrated error - configs.Slot0.kD = 0; // No output for error derivative + configs.Slot0.kS = 0.1; // To account for friction, add 0.1 V of static feedforward + configs.Slot0.kV = 0.12; // Kraken X60 is a 500 kV motor, 500 rpm per V = 8.333 rps per V, 1/8.33 = 0.12 volts / rotation per second + configs.Slot0.kP = 0.11; // An error of 1 rotation per second results in 0.11 V output + configs.Slot0.kI = 0; // No output for integrated error + configs.Slot0.kD = 0; // No output for error derivative // Peak output of 8 volts configs.Voltage.withPeakForwardVoltage(Volts.of(8)) .withPeakReverseVoltage(Volts.of(-8)); @@ -80,6 +79,9 @@ public Robot() { } m_fllr.setControl(new Follower(m_fx.getDeviceID(), MotorAlignmentValue.Aligned)); + + SmartDashboard.putNumber("desired RPS", 0); + SmartDashboard.putNumber("current RPS", m_fx.getVelocity().getValueAsDouble()); } @Override @@ -113,6 +115,9 @@ public void teleopPeriodic() { /* Disable the motor instead */ m_fx.setControl(m_brake); } + + SmartDashboard.putNumber("desired RPS", desiredRotationsPerSecond); + SmartDashboard.putNumber("current RPS", m_fx.getVelocity().getValueAsDouble()); } @Override diff --git a/prototype_projects/fuelintake/Fuel Intake/.gitignore b/prototype_projects/fuelintake/Fuel Intake/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/fuelintake/Fuel Intake/.vscode/launch.json b/prototype_projects/fuelintake/Fuel Intake/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/fuelintake/Fuel Intake/.vscode/settings.json b/prototype_projects/fuelintake/Fuel Intake/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/fuelintake/Fuel Intake/.wpilib/wpilib_preferences.json b/prototype_projects/fuelintake/Fuel Intake/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..994eeed --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/prototype_projects/fuelintake/Fuel Intake/WPILib-License.md b/prototype_projects/fuelintake/Fuel Intake/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/fuelintake/Fuel Intake/build.gradle b/prototype_projects/fuelintake/Fuel Intake/build.gradle new file mode 100644 index 0000000..f2ceadb --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/fuelintake/Fuel Intake/gradlew b/prototype_projects/fuelintake/Fuel Intake/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/fuelintake/Fuel Intake/gradlew.bat b/prototype_projects/fuelintake/Fuel Intake/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/fuelintake/Fuel Intake/settings.gradle b/prototype_projects/fuelintake/Fuel Intake/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/deploy/example.txt b/prototype_projects/fuelintake/Fuel Intake/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Constants.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..c50ba05 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Constants.java @@ -0,0 +1,19 @@ +// 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 frc.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 class OperatorConstants { + public static final int kDriverControllerPort = 0; + } +} diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Main.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Robot.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..2276443 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/Robot.java @@ -0,0 +1,108 @@ +// 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 frc.robot; + +import java.security.KeyStore.PrivateKeyEntry; + +import com.ctre.phoenix6.hardware.TalonFX; + +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; + +/** + * The methods in this class are called automatically corresponding to each mode, as described in + * the TimedRobot documentation. If you change the name of this class or the package after creating + * this project, you must also update the Main.java file in the project. + */ +public class Robot extends TimedRobot { + private Command m_autonomousCommand; + + private final RobotContainer m_robotContainer; + + private final TalonFX intakeMotor = new TalonFX(10); + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + public Robot() { + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); + } + + /** + * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics + * that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @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(); + intakeMotor.set(-.5); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); + + // schedule the autonomous command (example) + if (m_autonomousCommand != null) { + CommandScheduler.getInstance().schedule(m_autonomousCommand); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} + + /** This function is called once when the robot is first started up. */ + @Override + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} +} diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/RobotContainer.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..a33249e --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,63 @@ +// 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 frc.robot; + +import frc.robot.Constants.OperatorConstants; +import frc.robot.commands.Autos; +import frc.robot.commands.ExampleCommand; +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; + +/** + * 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 trigger mappings) should be declared here. + */ +public class RobotContainer { + // The robot's subsystems and commands are defined here... + private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); + + // Replace with CommandPS4Controller or CommandJoystick if needed + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + // Configure the trigger bindings + configureBindings(); + } + + /** + * Use this method to define your trigger->command mappings. Triggers can be created via the + * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary + * predicate, or via the named factories in {@link + * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link + * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller + * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight + * joysticks}. + */ + private void configureBindings() { + // Schedule `ExampleCommand` when `exampleCondition` changes to `true` + new Trigger(m_exampleSubsystem::exampleCondition) + .onTrue(new ExampleCommand(m_exampleSubsystem)); + + // Schedule `exampleMethodCommand` when the Xbox controller's B button is pressed, + // cancelling on release. + m_driverController.b().whileTrue(m_exampleSubsystem.exampleMethodCommand()); + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + // An example command will be run in autonomous + return Autos.exampleAuto(m_exampleSubsystem); + } +} diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/Autos.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/Autos.java @@ -0,0 +1,20 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; + +public final class Autos { + /** Example static factory for an autonomous command. */ + public static Command exampleAuto(ExampleSubsystem subsystem) { + return Commands.sequence(subsystem.exampleMethodCommand(), new ExampleCommand(subsystem)); + } + + private Autos() { + throw new UnsupportedOperationException("This is a utility class!"); + } +} diff --git a/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/ExampleCommand.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..3c5b9b4 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/commands/ExampleCommand.java @@ -0,0 +1,43 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; + +/** An example command that uses an example subsystem. */ +public class ExampleCommand extends Command { + @SuppressWarnings("PMD.UnusedPrivateField") + private final ExampleSubsystem m_subsystem; + + /** + * Creates a new ExampleCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExampleCommand(ExampleSubsystem subsystem) { + m_subsystem = subsystem; + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(subsystem); + } + + // 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() {} + + // 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/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..6b375da --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/src/main/java/frc/robot/subsystems/ExampleSubsystem.java @@ -0,0 +1,47 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class ExampleSubsystem extends SubsystemBase { + /** Creates a new ExampleSubsystem. */ + public ExampleSubsystem() {} + + /** + * Example command factory method. + * + * @return a command + */ + public Command exampleMethodCommand() { + // Inline construction of command goes here. + // Subsystem::RunOnce implicitly requires `this` subsystem. + return runOnce( + () -> { + /* one-time action goes here */ + }); + } + + /** + * An example method querying a boolean state of the subsystem (for example, a digital sensor). + * + * @return value of some boolean subsystem state, such as a digital sensor. + */ + public boolean exampleCondition() { + // Query some boolean state, such as a digital sensor. + return false; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/fuelintake/Fuel Intake/vendordeps/Phoenix6-26.1.0.json b/prototype_projects/fuelintake/Fuel Intake/vendordeps/Phoenix6-26.1.0.json new file mode 100644 index 0000000..dc5dc62 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/vendordeps/Phoenix6-26.1.0.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-26.1.0.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.0", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/fuelintake/Fuel Intake/vendordeps/REVLib.json b/prototype_projects/fuelintake/Fuel Intake/vendordeps/REVLib.json new file mode 100644 index 0000000..d35e593 --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/vendordeps/REVLib.json @@ -0,0 +1,133 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2026.0.1", + "frcYear": "2026", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2026.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2026.0.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2026.0.1", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.1", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.1", + "libName": "BackendDriver", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.1", + "libName": "REVLibWpi", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/fuelintake/Fuel Intake/vendordeps/WPILibNewCommands.json b/prototype_projects/fuelintake/Fuel Intake/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/fuelintake/Fuel Intake/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/launcher_kraken/.gitignore b/prototype_projects/launcher_kraken/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/launcher_kraken/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/launcher_kraken/.vscode/launch.json b/prototype_projects/launcher_kraken/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/launcher_kraken/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/launcher_kraken/.vscode/settings.json b/prototype_projects/launcher_kraken/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/launcher_kraken/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/launcher_kraken/.wpilib/wpilib_preferences.json b/prototype_projects/launcher_kraken/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..eadec71 --- /dev/null +++ b/prototype_projects/launcher_kraken/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 7762 +} diff --git a/prototype_projects/launcher_kraken/WPILib-License.md b/prototype_projects/launcher_kraken/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/launcher_kraken/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/launcher_kraken/build.gradle b/prototype_projects/launcher_kraken/build.gradle new file mode 100644 index 0000000..e9b0020 --- /dev/null +++ b/prototype_projects/launcher_kraken/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = true + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/launcher_kraken/elastic-layout.json b/prototype_projects/launcher_kraken/elastic-layout.json new file mode 100644 index 0000000..56a1b38 --- /dev/null +++ b/prototype_projects/launcher_kraken/elastic-layout.json @@ -0,0 +1,158 @@ +{ + "version": 1.0, + "grid_size": 128, + "tabs": [ + { + "name": "Teleoperated", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Actual Velocity", + "x": 0.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Actual Velocity", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Target Velocity", + "x": 128.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Target Velocity", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Reset Encoder", + "x": 256.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Reset Encoder", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Actual Velocity", + "x": 384.0, + "y": 0.0, + "width": 640.0, + "height": 384.0, + "type": "Graph", + "properties": { + "topic": "/SmartDashboard/Actual Velocity", + "period": 0.033, + "data_type": "double", + "time_displayed": 5.0, + "min_value": 0.0, + "max_value": 4000.0, + "color": 4278238420, + "line_width": 2.0 + } + }, + { + "title": "Target Velocity", + "x": 0.0, + "y": 128.0, + "width": 384.0, + "height": 256.0, + "type": "Graph", + "properties": { + "topic": "/SmartDashboard/Target Velocity", + "period": 0.033, + "data_type": "double", + "time_displayed": 5.0, + "min_value": 0.0, + "max_value": 2000.0, + "color": 4278238420, + "line_width": 2.0 + } + }, + { + "title": "set RPS", + "x": 512.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/set RPS", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "current RPS2", + "x": 768.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/current RPS2", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "current RPS1", + "x": 640.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/current RPS1", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "desired RPS", + "x": 896.0, + "y": 384.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/desired RPS", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + } + ] + } + }, + { + "name": "Autonomous", + "grid_layout": { + "layouts": [], + "containers": [] + } + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/fuleintake/.gitignore b/prototype_projects/launcher_kraken/fuleintake/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/launcher_kraken/fuleintake/.vscode/launch.json b/prototype_projects/launcher_kraken/fuleintake/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/launcher_kraken/fuleintake/.vscode/settings.json b/prototype_projects/launcher_kraken/fuleintake/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/launcher_kraken/fuleintake/.wpilib/wpilib_preferences.json b/prototype_projects/launcher_kraken/fuleintake/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..994eeed --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/fuleintake/WPILib-License.md b/prototype_projects/launcher_kraken/fuleintake/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/launcher_kraken/fuleintake/build.gradle b/prototype_projects/launcher_kraken/fuleintake/build.gradle new file mode 100644 index 0000000..f2ceadb --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/launcher_kraken/fuleintake/gradlew b/prototype_projects/launcher_kraken/fuleintake/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/launcher_kraken/fuleintake/gradlew.bat b/prototype_projects/launcher_kraken/fuleintake/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/launcher_kraken/fuleintake/settings.gradle b/prototype_projects/launcher_kraken/fuleintake/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/launcher_kraken/fuleintake/src/main/deploy/example.txt b/prototype_projects/launcher_kraken/fuleintake/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Main.java b/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Robot.java b/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..5c7b2cf --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/src/main/java/frc/robot/Robot.java @@ -0,0 +1,57 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.Joystick; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; + +/** + * This sample program shows how to control a motor using a joystick. In the operator control part + * of the program, the joystick is read and the value is written to the motor. + * + *

Joystick analog values range from -1 to 1 and motor controller inputs also range from -1 to 1 + * making it easy to work together. + * + *

In addition, the encoder value of an encoder connected to ports 0 and 1 is consistently sent + * to the Dashboard. + */ +public class Robot extends TimedRobot { + private static final int kMotorPort = 0; + private static final int kJoystickPort = 0; + private static final int kEncoderPortA = 0; + private static final int kEncoderPortB = 1; + + private final PWMSparkMax m_motor; + private final Joystick m_joystick; + private final Encoder m_encoder; + + /** Called once at the beginning of the robot program. */ + public Robot() { + m_motor = new PWMSparkMax(kMotorPort); + m_joystick = new Joystick(kJoystickPort); + m_encoder = new Encoder(kEncoderPortA, kEncoderPortB); + // Use SetDistancePerPulse to set the multiplier for GetDistance + // This is set up assuming a 6 inch wheel with a 360 CPR encoder. + m_encoder.setDistancePerPulse((Math.PI * 6) / 360.0); + } + + /* + * The RobotPeriodic function is called every control packet no matter the + * robot mode. + */ + @Override + public void robotPeriodic() { + SmartDashboard.putNumber("Encoder", m_encoder.getDistance()); + } + + /** The teleop periodic function is called every control packet in teleop. */ + @Override + public void teleopPeriodic() { + m_motor.set(m_joystick.getY()); + } +} diff --git a/prototype_projects/launcher_kraken/fuleintake/vendordeps/WPILibNewCommands.json b/prototype_projects/launcher_kraken/fuleintake/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/launcher_kraken/fuleintake/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/launcher_kraken/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/launcher_kraken/gradlew b/prototype_projects/launcher_kraken/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/launcher_kraken/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/launcher_kraken/gradlew.bat b/prototype_projects/launcher_kraken/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/launcher_kraken/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/launcher_kraken/settings.gradle b/prototype_projects/launcher_kraken/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/launcher_kraken/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/launcher_kraken/src/main/deploy/example.txt b/prototype_projects/launcher_kraken/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/Constants.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..7373aca --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Constants.java @@ -0,0 +1,38 @@ +// 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 frc.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 class LauncherConstants { + public static final int kMotorCANID1= 41; + public static final int kMotorCANID2 = 40; + + // These numbers came from the ctre example then tweaked + public static final double kS = 0.1; // A velocity target of 1 rps results in xV output + public static final double kV = 0.12; // Add x V output to overcome static friction + public static final double kP = 0.11; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kA_linear = 0.01; // Voltage needed to induce a given accel. in the motor shaft + public static final double kA_angular = 0.01; // TODO: We need to measure this! + public static final double kPeakVoltage = 8.0; + + public static final double kMaxVelocityRPS = 6000.0/60.0; + public static final double kMaxAccelerationRPS2 = 50.0; + + public static final double kMotionMagicAcceleration = 50.0; // Higher number --> Faster (50.0 = ~1s to max) + public static final double kMotionMagicJerk = 4000.0; + } +} diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/Main.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/Mechanisms.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Mechanisms.java new file mode 100644 index 0000000..572e396 --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Mechanisms.java @@ -0,0 +1,58 @@ +package frc.robot; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.StatusSignal; + +import edu.wpi.first.units.measure.Angle; +import edu.wpi.first.units.measure.AngularVelocity; +import edu.wpi.first.wpilibj.smartdashboard.Mechanism2d; +import edu.wpi.first.wpilibj.smartdashboard.MechanismLigament2d; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj.util.Color; +import edu.wpi.first.wpilibj.util.Color8Bit; + +/** + * Class to keep all the mechanism-specific objects together and out of the main example + */ +public class Mechanisms { + double HEIGHT = 1; // Controls the height of the mech2d SmartDashboard + double WIDTH = 1; // Controls the height of the mech2d SmartDashboard + + Mechanism2d mech = new Mechanism2d(WIDTH, HEIGHT); + // Velocity + MechanismLigament2d VelocityMech = mech. + getRoot("velocityLineReferencePosition", 0.75, 0.5). + append(new MechanismLigament2d("velocityLine", 1,90, 6, new Color8Bit(Color.kAliceBlue))); + + MechanismLigament2d midline = mech. + getRoot("midline", 0.7, 0.5). + append(new MechanismLigament2d("midline", 0.1, 0, 3, new Color8Bit(Color.kCyan))); + + //Position + MechanismLigament2d arm = mech. + getRoot("pivotPoint", 0.25, 0.5). + append(new MechanismLigament2d("arm", .2, 0, 0, new Color8Bit(Color.kAliceBlue))); + + MechanismLigament2d side1 = arm.append(new MechanismLigament2d("side1", 0.15307, 112.5, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side2 = side1.append(new MechanismLigament2d("side2", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side3 = side2.append(new MechanismLigament2d("side3", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side4 = side3.append(new MechanismLigament2d("side4", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side5 = side4.append(new MechanismLigament2d("side5", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side6 = side5.append(new MechanismLigament2d("side6", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side7 = side6.append(new MechanismLigament2d("side7", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + MechanismLigament2d side8 = side7.append(new MechanismLigament2d("side8", 0.15307, 45, 6, new Color8Bit(Color.kAliceBlue))); + + /** + * Runs the mech2d widget in GUI. + * + * This utilizes GUI to simulate and display a TalonFX and exists to allow users to test and understand + * features of our products in simulation using our examples out of the box. Users may modify to have a + * display interface that they find more intuitive or visually appealing. + */ + public void update(StatusSignal position, StatusSignal velocity) { + VelocityMech.setLength(velocity.getValue().in(RotationsPerSecond)/120); + arm.setAngle(position.getValue().in(Rotations) * 360); // Divide by 120 to scale motion to fit in the window + SmartDashboard.putData("mech2d", mech); // Creates mech2d in SmartDashboard + } +} diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/Robot.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..8b4f76c --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/Robot.java @@ -0,0 +1,120 @@ +// 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 frc.robot; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.StatusCode; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.controls.MotionMagicVelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.robot.Constants.LauncherConstants; + +/** + * The VM is configured to automatically run this class, and to call the functions corresponding to + * each mode, as described in the TimedRobot documentation. If you change the name of this class or + * the package after creating this project, you must also update the build.gradle file in the + * project. + */ +public class Robot extends TimedRobot { + private final TalonFX m_fx1 = new TalonFX(LauncherConstants.kMotorCANID1); + private final TalonFX m_fx2 = new TalonFX(LauncherConstants.kMotorCANID2); + + /* Be able to switch which control request to use based on a button press */ + /* Start at velocity 0, use slot 0 */ + private final MotionMagicVelocityVoltage m_velocityVoltage = new MotionMagicVelocityVoltage(0).withSlot(0); + private final XboxController m_joystick = new XboxController(0); + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + public Robot() { + TalonFXConfiguration configs = new TalonFXConfiguration(); + + + /* Voltage-based velocity requires a velocity feed forward to account for the back-emf of the motor */ + configs.Slot0.kS = LauncherConstants.kS; // To account for friction, add 0.1 V of static feedforward + configs.Slot0.kV = LauncherConstants.kV; // Kraken X60 is a 500 kV motor, 500 rpm per V = 8.333 rps per V, 1/8.33 = 0.12 volts / rotation per second + configs.Slot0.kP = LauncherConstants.kP; // An error of 1 rotation per second results in 0.11 V output + configs.Slot0.kI = LauncherConstants.kI; // No output for integrated error + configs.Slot0.kD = LauncherConstants.kD; // No output for error derivative + // Peak output of 8 volts + configs.Voltage.withPeakForwardVoltage(Volts.of(8)) + .withPeakReverseVoltage(Volts.of(-8)); + + var motionMagicConfigs = configs.MotionMagic; + motionMagicConfigs.MotionMagicAcceleration = LauncherConstants.kMotionMagicAcceleration; + motionMagicConfigs.MotionMagicJerk = LauncherConstants.kMotionMagicJerk; + + /* Retry config apply up to 5 times, report if failure */ + StatusCode status = StatusCode.StatusCodeNotInitialized; + for (int i = 0; i < 5; ++i) { + status = m_fx1.getConfigurator().apply(configs); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs 1, error code: " + status.toString()); + } + for (int i = 0; i < 5; ++i) { + status = m_fx2.getConfigurator().apply(configs); + if (status.isOK()) break; + } + if (!status.isOK()) { + System.out.println("Could not apply configs 2, error code: " + status.toString()); + } + + SmartDashboard.putNumber("desired RPS", 0); + SmartDashboard.getNumber("desired RPS", 0); + SmartDashboard.putNumber("current RPS1", m_fx1.getVelocity().getValueAsDouble()); + SmartDashboard.putNumber("current RPS2", m_fx2.getVelocity().getValueAsDouble()); + } + + @Override + public void robotPeriodic() { + } + + @Override + public void autonomousInit() {} + + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() {} + + @Override + public void teleopPeriodic() { + double joyValue = m_joystick.getLeftY(); + if (Math.abs(joyValue) < 0.1) joyValue = 0; + + double desiredRotationsPerSecond = joyValue * (6000/60); // Control motor from -6000 to +6000 rpm + + desiredRotationsPerSecond = SmartDashboard.getNumber("desired RPS", desiredRotationsPerSecond); + SmartDashboard.putNumber("set RPS", desiredRotationsPerSecond); + + /* Use velocity voltage */ + m_fx1.setControl(m_velocityVoltage.withVelocity(desiredRotationsPerSecond)); + m_fx2.setControl(m_velocityVoltage.withVelocity(-desiredRotationsPerSecond)); + + SmartDashboard.putNumber("current RPS1", m_fx1.getVelocity().getValueAsDouble()); + SmartDashboard.putNumber("current RPS2", m_fx2.getVelocity().getValueAsDouble()); + } + + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + @Override + public void testInit() {} + + @Override + public void testPeriodic() {} +} diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/PhysicsSim.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/PhysicsSim.java new file mode 100644 index 0000000..6000bca --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/PhysicsSim.java @@ -0,0 +1,81 @@ +package frc.robot.sim; + +import java.util.ArrayList; + +import com.ctre.phoenix6.Utils; +import com.ctre.phoenix6.hardware.TalonFX; + +/** + * Manages physics simulation for CTRE products. + */ +public class PhysicsSim { + private static final PhysicsSim sim = new PhysicsSim(); + + /** + * Gets the robot simulator instance. + */ + public static PhysicsSim getInstance() { + return sim; + } + + /** + * Adds a TalonFX controller to the simulator. + * + * @param talonFX + * The TalonFX device + * @param rotorInertia + * Rotational Inertia of the mechanism at the rotor + */ + public void addTalonFX(TalonFX talonFX, final double rotorInertia) { + if (talonFX != null) { + TalonFXSimProfile simTalonFX = new TalonFXSimProfile(talonFX, rotorInertia); + _simProfiles.add(simTalonFX); + } + } + + /** + * Runs the simulator: + * - enable the robot + * - simulate sensors + */ + public void run() { + // Simulate devices + for (SimProfile simProfile : _simProfiles) { + simProfile.run(); + } + } + + private final ArrayList _simProfiles = new ArrayList(); + + /** + * Holds information about a simulated device. + */ + static class SimProfile { + private double _lastTime; + private boolean _running = false; + + /** + * Runs the simulation profile. + * Implemented by device-specific profiles. + */ + public void run() { + } + + /** + * Returns the time since last call, in seconds. + */ + protected double getPeriod() { + // set the start time if not yet running + if (!_running) { + _lastTime = Utils.getCurrentTimeSeconds(); + _running = true; + } + + double now = Utils.getCurrentTimeSeconds(); + final double period = now - _lastTime; + _lastTime = now; + + return period; + } + } +} \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/TalonFXSimProfile.java b/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/TalonFXSimProfile.java new file mode 100644 index 0000000..0dc9756 --- /dev/null +++ b/prototype_projects/launcher_kraken/src/main/java/frc/robot/sim/TalonFXSimProfile.java @@ -0,0 +1,57 @@ +package frc.robot.sim; + +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.sim.TalonFXSimState; + +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.math.system.plant.LinearSystemId; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.simulation.DCMotorSim; +import frc.robot.sim.PhysicsSim.SimProfile; + +/** + * Holds information about a simulated TalonFX. + */ +class TalonFXSimProfile extends SimProfile { + private static final double kMotorResistance = 0.002; // Assume 2mOhm resistance for voltage drop calculation + private final TalonFXSimState _talonFXSim; + private final DCMotorSim _motorSim; + + /** + * Creates a new simulation profile for a TalonFX device. + * + * @param talonFX + * The TalonFX device + * @param rotorInertia + * Rotational Inertia of the mechanism at the rotor + */ + public TalonFXSimProfile(final TalonFX talonFX, final double rotorInertia) { + var gearbox = DCMotor.getKrakenX60Foc(1); + this._motorSim = new DCMotorSim(LinearSystemId.createDCMotorSystem(gearbox, rotorInertia, 1.0), gearbox); + this._talonFXSim = talonFX.getSimState(); + } + + /** + * Runs the simulation profile. + * + * This uses very rudimentary physics simulation and exists to allow users to + * test features of our products in simulation using our examples out of the + * box. Users may modify this to utilize more accurate physics simulation. + */ + public void run() { + /// DEVICE SPEED SIMULATION + + _motorSim.setInputVoltage(_talonFXSim.getMotorVoltage()); + + _motorSim.update(getPeriod()); + + /// SET SIM PHYSICS INPUTS + final double position_rot = _motorSim.getAngularPositionRotations(); + final double velocity_rps = Units.radiansToRotations(_motorSim.getAngularVelocityRadPerSec()); + + _talonFXSim.setRawRotorPosition(position_rot); + _talonFXSim.setRotorVelocity(velocity_rps); + + _talonFXSim.setSupplyVoltage(12 - _talonFXSim.getSupplyCurrent() * kMotorResistance); + } +} \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/vendordeps/Phoenix6-frc2026-latest.json b/prototype_projects/launcher_kraken/vendordeps/Phoenix6-frc2026-latest.json new file mode 100644 index 0000000..8f6e30f --- /dev/null +++ b/prototype_projects/launcher_kraken/vendordeps/Phoenix6-frc2026-latest.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-frc2026-latest.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.0", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_kraken/vendordeps/WPILibNewCommands.json b/prototype_projects/launcher_kraken/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/launcher_kraken/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/launcher_neo/.gitignore b/prototype_projects/launcher_neo/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/launcher_neo/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/launcher_neo/.vscode/launch.json b/prototype_projects/launcher_neo/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/launcher_neo/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/launcher_neo/.vscode/settings.json b/prototype_projects/launcher_neo/.vscode/settings.json new file mode 100644 index 0000000..612cdd0 --- /dev/null +++ b/prototype_projects/launcher_neo/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ] +} diff --git a/prototype_projects/launcher_neo/.wpilib/wpilib_preferences.json b/prototype_projects/launcher_neo/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..6c8cf0e --- /dev/null +++ b/prototype_projects/launcher_neo/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 0 +} \ No newline at end of file diff --git a/prototype_projects/launcher_neo/README.md b/prototype_projects/launcher_neo/README.md new file mode 100644 index 0000000..9e5bb97 --- /dev/null +++ b/prototype_projects/launcher_neo/README.md @@ -0,0 +1,31 @@ +# MAXMotion Example + +## Description + +This example shows how to perform both position and velocity closed loop control using MAXMotion on a SPARK motor controller. + +### Topics Covered + +* Configuring a SPARK motor controller +* Position closed loop control with MAXMotion +* Velocity closed loop control with MAXMotion +* Closed loop slots +* Retrieving encoder data +* Resetting encoder position + +## Usage + +This example assumes a SPARK MAX with a free spinning NEO. The PID values are tuned for this and should be adjusted if a different setup is being used. + + + +Deploy the program to your roboRIO and load the included `shuffleboard.json` into Shuffleboard. The Shuffleboard layout provides the following: + +* A toggle to switch between position and velocity control modes +* Input sliders to set target position or velocity +* Output sliders to show actual position and velocity +* A reset button for the encoder position + +### AdvantageScope + +To better visualize the motion profile created by MAXMotion, load the included `advantageScope.json` into AdvantageScope. The AdvantageScope layout graphs the target and actual position/velocity. diff --git a/prototype_projects/launcher_neo/WPILib-License.md b/prototype_projects/launcher_neo/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/launcher_neo/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/launcher_neo/advantageScope.json b/prototype_projects/launcher_neo/advantageScope.json new file mode 100644 index 0000000..38234c2 --- /dev/null +++ b/prototype_projects/launcher_neo/advantageScope.json @@ -0,0 +1,99 @@ +{ + "hubs": [ + { + "x": 496, + "y": 240, + "width": 1100, + "height": 650, + "state": { + "sidebar": { + "width": 193, + "expanded": [ + "/SmartDashboard" + ] + }, + "tabs": { + "selected": 1, + "tabs": [ + { + "type": 0, + "title": "", + "controller": null, + "controllerUUID": "k0nrll5jj791ndnm9k71vwish5qhrwyh", + "renderer": "#/", + "controlsHeight": 0 + }, + { + "type": 1, + "title": "Line Graph", + "controller": { + "leftSources": [ + { + "type": "stepped", + "logKey": "NT:/SmartDashboard/Actual Velocity", + "logType": "Number", + "visible": true, + "options": { + "color": "#2b66a2", + "size": "normal" + } + }, + { + "type": "stepped", + "logKey": "NT:/SmartDashboard/Target Velocity", + "logType": "Number", + "visible": true, + "options": { + "color": "#af2437", + "size": "normal" + } + } + ], + "rightSources": [ + { + "type": "stepped", + "logKey": "NT:/SmartDashboard/Actual Position", + "logType": "Number", + "visible": true, + "options": { + "color": "#e5b31b", + "size": "normal" + } + }, + { + "type": "stepped", + "logKey": "NT:/SmartDashboard/Target Position", + "logType": "Number", + "visible": true, + "options": { + "color": "#80588e", + "size": "normal" + } + } + ], + "discreteSources": [], + "leftLockedRange": null, + "rightLockedRange": null, + "leftUnitConversion": { + "type": null, + "factor": 1 + }, + "rightUnitConversion": { + "type": null, + "factor": 1 + }, + "leftFilter": 0, + "rightFilter": 0 + }, + "controllerUUID": "5y6e5qp2x5cjij2vixdlikkwfwhrgr3j", + "renderer": null, + "controlsHeight": 200 + } + ] + } + } + } + ], + "satellites": [], + "version": "4.0.0-beta-1" +} diff --git a/prototype_projects/launcher_neo/build.gradle b/prototype_projects/launcher_neo/build.gradle new file mode 100644 index 0000000..f2ceadb --- /dev/null +++ b/prototype_projects/launcher_neo/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/launcher_neo/elastic/launcher.json b/prototype_projects/launcher_neo/elastic/launcher.json new file mode 100644 index 0000000..abd55ce --- /dev/null +++ b/prototype_projects/launcher_neo/elastic/launcher.json @@ -0,0 +1,102 @@ +{ + "version": 1.0, + "grid_size": 128, + "tabs": [ + { + "name": "Teleoperated", + "grid_layout": { + "layouts": [], + "containers": [ + { + "title": "Actual Velocity", + "x": 0.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Actual Velocity", + "period": 0.06, + "data_type": "double", + "show_submit_button": false + } + }, + { + "title": "Target Velocity", + "x": 128.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Text Display", + "properties": { + "topic": "/SmartDashboard/Target Velocity", + "period": 0.06, + "data_type": "double", + "show_submit_button": true + } + }, + { + "title": "Reset Encoder", + "x": 256.0, + "y": 0.0, + "width": 128.0, + "height": 128.0, + "type": "Boolean Box", + "properties": { + "topic": "/SmartDashboard/Reset Encoder", + "period": 0.06, + "data_type": "boolean", + "true_color": 4283215696, + "false_color": 4294198070, + "true_icon": "None", + "false_icon": "None" + } + }, + { + "title": "Actual Velocity", + "x": 384.0, + "y": 0.0, + "width": 640.0, + "height": 384.0, + "type": "Graph", + "properties": { + "topic": "/SmartDashboard/Actual Velocity", + "period": 0.033, + "data_type": "double", + "time_displayed": 5.0, + "min_value": 0.0, + "max_value": 2000.0, + "color": 4278238420, + "line_width": 2.0 + } + }, + { + "title": "Target Velocity", + "x": 0.0, + "y": 128.0, + "width": 384.0, + "height": 256.0, + "type": "Graph", + "properties": { + "topic": "/SmartDashboard/Target Velocity", + "period": 0.033, + "data_type": "double", + "time_displayed": 5.0, + "min_value": 0.0, + "max_value": 2000.0, + "color": 4278238420, + "line_width": 2.0 + } + } + ] + } + }, + { + "name": "Autonomous", + "grid_layout": { + "layouts": [], + "containers": [] + } + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/launcher_neo/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/launcher_neo/gradlew b/prototype_projects/launcher_neo/gradlew new file mode 100755 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/launcher_neo/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/launcher_neo/gradlew.bat b/prototype_projects/launcher_neo/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/launcher_neo/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/launcher_neo/settings.gradle b/prototype_projects/launcher_neo/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/launcher_neo/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/launcher_neo/shuffleboard.json b/prototype_projects/launcher_neo/shuffleboard.json new file mode 100644 index 0000000..15e9980 --- /dev/null +++ b/prototype_projects/launcher_neo/shuffleboard.json @@ -0,0 +1,135 @@ +{ + "tabPane": [ + { + "title": "SmartDashboard", + "autoPopulate": true, + "autoPopulatePrefix": "SmartDashboard/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "titleType": 0, + "tiles": { + "0,0": { + "size": [ + 3, + 1 + ], + "content": { + "_type": "Number Slider", + "_source0": "network_table:///SmartDashboard/Target Position", + "_title": "Target Position", + "_glyph": 148, + "_showGlyph": false, + "Slider Settings/Min": 0.0, + "Slider Settings/Max": 100.0, + "Slider Settings/Block increment": 0.0625, + "Visuals/Display value": true, + "Visuals/Orientation": "HORIZONTAL" + } + }, + "4,0": { + "size": [ + 3, + 1 + ], + "content": { + "_type": "Number Slider", + "_source0": "network_table:///SmartDashboard/Target Velocity", + "_title": "Target Velocity", + "_glyph": 148, + "_showGlyph": false, + "Slider Settings/Min": -6000.0, + "Slider Settings/Max": 6000.0, + "Slider Settings/Block increment": 0.0625, + "Visuals/Display value": true, + "Visuals/Orientation": "HORIZONTAL" + } + }, + "3,0": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Toggle Switch", + "_source0": "network_table:///SmartDashboard/Control Mode", + "_title": "Control Mode", + "_glyph": 148, + "_showGlyph": false + } + }, + "0,1": { + "size": [ + 3, + 1 + ], + "content": { + "_type": "Number Slider", + "_source0": "network_table:///SmartDashboard/Actual Position", + "_title": "Actual Position", + "_glyph": 148, + "_showGlyph": false, + "Slider Settings/Min": 0.0, + "Slider Settings/Max": 100.0, + "Slider Settings/Block increment": 0.0625, + "Visuals/Display value": true, + "Visuals/Orientation": "HORIZONTAL" + } + }, + "4,1": { + "size": [ + 3, + 1 + ], + "content": { + "_type": "Number Slider", + "_source0": "network_table:///SmartDashboard/Actual Velocity", + "_title": "Actual Velocity", + "_glyph": 148, + "_showGlyph": false, + "Slider Settings/Min": -6000.0, + "Slider Settings/Max": 6000.0, + "Slider Settings/Block increment": 0.0625, + "Visuals/Display value": true, + "Visuals/Orientation": "HORIZONTAL" + } + }, + "3,1": { + "size": [ + 1, + 1 + ], + "content": { + "_type": "Toggle Button", + "_source0": "network_table:///SmartDashboard/Reset Encoder", + "_title": "Reset Encoder", + "_glyph": 148, + "_showGlyph": false + } + } + } + } + }, + { + "title": "LiveWindow", + "autoPopulate": true, + "autoPopulatePrefix": "LiveWindow/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "titleType": 0, + "tiles": {} + } + } + ], + "windowGeometry": { + "x": -8.0, + "y": -8.0, + "width": 1936.0, + "height": 1048.0 + } +} \ No newline at end of file diff --git a/prototype_projects/launcher_neo/src/main/deploy/example.txt b/prototype_projects/launcher_neo/src/main/deploy/example.txt new file mode 100644 index 0000000..70c79b6 --- /dev/null +++ b/prototype_projects/launcher_neo/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'FileUtilities.getFilePath' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/launcher_neo/src/main/java/frc/robot/Constants.java b/prototype_projects/launcher_neo/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..9e54a86 --- /dev/null +++ b/prototype_projects/launcher_neo/src/main/java/frc/robot/Constants.java @@ -0,0 +1,30 @@ +// 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 frc.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 class LauncherConstants { + public static final int kMotorCANID = 50; + + // These numbers came from the ctre example then tweaked + public static final double kP = 0.0001; // An error of 1 rotation results in x V output + public static final double kI = 0.0; + public static final double kD = 0.0; // A velocity of 1 rps results in x V output + public static final double kV = (12.0/40000.0); // Expected value is (12.0/5767) but this yields a huge offset (100rpm setpoint, 700rpm actual) + public static final double kCruiseVelocityRPM = 5700.0; // Note actually needed during velocity control. + public static final double kAllowedErrorR = 10.0; + public static final double kMaxAccelerationRPMPS = 1000.0; + } +} diff --git a/prototype_projects/launcher_neo/src/main/java/frc/robot/Main.java b/prototype_projects/launcher_neo/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..5b3238a --- /dev/null +++ b/prototype_projects/launcher_neo/src/main/java/frc/robot/Main.java @@ -0,0 +1,29 @@ +/*----------------------------------------------------------------------------*/ +/* 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. + * Unless you know what you are doing, do not modify this file except to + * change the parameter class to the startRobot call. + */ +public final class Main { + private Main() { + } + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/launcher_neo/src/main/java/frc/robot/Robot.java b/prototype_projects/launcher_neo/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..3c9393d --- /dev/null +++ b/prototype_projects/launcher_neo/src/main/java/frc/robot/Robot.java @@ -0,0 +1,116 @@ +/*----------------------------------------------------------------------------*/ +/* 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. */ +/*----------------------------------------------------------------------------*/ + +// Based on the REV Robotics MaxMotion example application. + +package frc.robot; + +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.robot.Constants.LauncherConstants; + +import com.revrobotics.PersistMode; +import com.revrobotics.RelativeEncoder; +import com.revrobotics.ResetMode; +import com.revrobotics.spark.SparkLowLevel.MotorType; +import com.revrobotics.spark.SparkClosedLoopController; +import com.revrobotics.spark.SparkMax; +import com.revrobotics.spark.SparkBase.ControlType; +import com.revrobotics.spark.config.SparkMaxConfig; +import com.revrobotics.spark.ClosedLoopSlot; +import com.revrobotics.spark.FeedbackSensor; + +public class Robot extends TimedRobot { + private SparkMax motor; + private SparkMaxConfig motorConfig; + private SparkClosedLoopController closedLoopController; + private RelativeEncoder encoder; + + public Robot() { + /* + * Initialize the SPARK MAX and get its encoder and closed loop controller + * objects for later use. + */ + motor = new SparkMax(LauncherConstants.kMotorCANID, MotorType.kBrushless); + closedLoopController = motor.getClosedLoopController(); + encoder = motor.getEncoder(); + + /* + * Create a new SPARK MAX configuration object. This will store the + * configuration parameters for the SPARK MAX that we will set below. + */ + motorConfig = new SparkMaxConfig(); + + /* + * Configure the encoder. For this specific example, we are using the + * integrated encoder of the NEO, and we don't need to configure it. If + * needed, we can adjust values like the position or velocity conversion + * factors. + */ + motorConfig.encoder.velocityConversionFactor(1); + + /* + * Configure the closed loop controller. We want to make sure we set the + * feedback sensor as the primary encoder. + */ + motorConfig.closedLoop + .feedbackSensor(FeedbackSensor.kPrimaryEncoder) + // Set PID values for velocity control in slot 1 + .p(LauncherConstants.kP, ClosedLoopSlot.kSlot1) + .i(LauncherConstants.kI, ClosedLoopSlot.kSlot1) + .d(LauncherConstants.kD, ClosedLoopSlot.kSlot1) + .outputRange(-1, 1, ClosedLoopSlot.kSlot1) + .feedForward + .kV(LauncherConstants.kV, ClosedLoopSlot.kSlot1); + + motorConfig.closedLoop.maxMotion + // Set MAXMotion parameters for velocity control in slot 1 + .maxAcceleration(LauncherConstants.kMaxAccelerationRPMPS, ClosedLoopSlot.kSlot1) + .cruiseVelocity(LauncherConstants.kCruiseVelocityRPM, ClosedLoopSlot.kSlot1) + .allowedProfileError(LauncherConstants.kAllowedErrorR, ClosedLoopSlot.kSlot1); + + /* + * Apply the configuration to the SPARK MAX. + * + * kResetSafeParameters is used to get the SPARK MAX to a known state. This + * is useful in case the SPARK MAX is replaced. + * + * kPersistParameters is used to ensure the configuration is not lost when + * the SPARK MAX loses power. This is useful for power cycles that may occur + * mid-operation. + */ + motor.configure(motorConfig, ResetMode.kResetSafeParameters, PersistMode.kNoPersistParameters); + + // Initialize dashboard values + SmartDashboard.setDefaultNumber("Target Velocity", 0); + SmartDashboard.setDefaultBoolean("Reset Encoder", false); + } + + @Override + public void teleopPeriodic() { + /* + * Get the target velocity from SmartDashboard and set it as the setpoint + * for the closed loop controller with MAXMotionVelocityControl as the + * control type. + */ + double targetVelocity = SmartDashboard.getNumber("Target Velocity", 0); + closedLoopController.setSetpoint(targetVelocity, ControlType.kMAXMotionVelocityControl, + ClosedLoopSlot.kSlot1); + } + + @Override + public void robotPeriodic() { + // Display encoder position and velocity + SmartDashboard.putNumber("Actual Velocity", encoder.getVelocity()); + + if (SmartDashboard.getBoolean("Reset Encoder", false)) { + SmartDashboard.putBoolean("Reset Encoder", false); + // Reset the encoder position to 0 + encoder.setPosition(0); + } + } +} diff --git a/prototype_projects/launcher_neo/vendordeps/Phoenix6-26.1.1.json b/prototype_projects/launcher_neo/vendordeps/Phoenix6-26.1.1.json new file mode 100644 index 0000000..7a0eca0 --- /dev/null +++ b/prototype_projects/launcher_neo/vendordeps/Phoenix6-26.1.1.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-26.1.1.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.1", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.1", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.1", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.1", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.1", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.1", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.1", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.1", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.1", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.1", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.1", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.1", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.1", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.1", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.1", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_neo/vendordeps/REVLib.json b/prototype_projects/launcher_neo/vendordeps/REVLib.json new file mode 100644 index 0000000..4f96af8 --- /dev/null +++ b/prototype_projects/launcher_neo/vendordeps/REVLib.json @@ -0,0 +1,133 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2026.0.0", + "frcYear": "2026", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2026.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2026.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2026.0.0", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.0", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.0", + "libName": "BackendDriver", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.0", + "libName": "REVLibWpi", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_neo/vendordeps/WPILibNewCommands.json b/prototype_projects/launcher_neo/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/launcher_neo/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} diff --git a/prototype_projects/launcher_v1/.gitignore b/prototype_projects/launcher_v1/.gitignore new file mode 100644 index 0000000..34cbaac --- /dev/null +++ b/prototype_projects/launcher_v1/.gitignore @@ -0,0 +1,187 @@ +# This gitignore has been specially created by the WPILib team. +# If you remove items from this file, intellisense might break. + +### C++ ### +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# # VS Code Specific Java Settings +# DO NOT REMOVE .classpath and .project +.classpath +.project +.settings/ +bin/ + +# IntelliJ +*.iml +*.ipr +*.iws +.idea/ +out/ + +# Fleet +.fleet + +# Simulation GUI and other tools window save file +networktables.json +simgui.json +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ + +# clangd +/.cache +compile_commands.json + +# Eclipse generated file for annotation processors +.factorypath diff --git a/prototype_projects/launcher_v1/.vscode/launch.json b/prototype_projects/launcher_v1/.vscode/launch.json new file mode 100644 index 0000000..c9c9713 --- /dev/null +++ b/prototype_projects/launcher_v1/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "wpilib", + "name": "WPILib Desktop Debug", + "request": "launch", + "desktop": true, + }, + { + "type": "wpilib", + "name": "WPILib roboRIO Debug", + "request": "launch", + "desktop": false, + } + ] +} diff --git a/prototype_projects/launcher_v1/.vscode/settings.json b/prototype_projects/launcher_v1/.vscode/settings.json new file mode 100644 index 0000000..5e6ede8 --- /dev/null +++ b/prototype_projects/launcher_v1/.vscode/settings.json @@ -0,0 +1,61 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "java.server.launchMode": "Standard", + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "bin/": true, + "**/.classpath": true, + "**/.project": true, + "**/.settings": 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", + "java.import.gradle.annotationProcessing.enabled": false, + "java.completion.favoriteStaticMembers": [ + "org.junit.Assert.*", + "org.junit.Assume.*", + "org.junit.jupiter.api.Assertions.*", + "org.junit.jupiter.api.Assumptions.*", + "org.junit.jupiter.api.DynamicContainer.*", + "org.junit.jupiter.api.DynamicTest.*", + "org.mockito.Mockito.*", + "org.mockito.ArgumentMatchers.*", + "org.mockito.Answers.*", + "edu.wpi.first.units.Units.*" + ], + "java.completion.filteredTypes": [ + "java.awt.*", + "com.sun.*", + "sun.*", + "jdk.*", + "org.graalvm.*", + "io.micrometer.shaded.*", + "java.beans.*", + "java.util.Base64.*", + "java.util.Timer", + "java.sql.*", + "javax.swing.*", + "javax.management.*", + "javax.smartcardio.*", + "edu.wpi.first.math.proto.*", + "edu.wpi.first.math.**.proto.*", + "edu.wpi.first.math.**.struct.*", + ], + "java.dependency.enableDependencyCheckup": false +} diff --git a/prototype_projects/launcher_v1/.wpilib/wpilib_preferences.json b/prototype_projects/launcher_v1/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..994eeed --- /dev/null +++ b/prototype_projects/launcher_v1/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2026", + "teamNumber": 9577 +} \ No newline at end of file diff --git a/prototype_projects/launcher_v1/WPILib-License.md b/prototype_projects/launcher_v1/WPILib-License.md new file mode 100644 index 0000000..eb3061b --- /dev/null +++ b/prototype_projects/launcher_v1/WPILib-License.md @@ -0,0 +1,24 @@ +Copyright (c) 2009-2026 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/prototype_projects/launcher_v1/build.gradle b/prototype_projects/launcher_v1/build.gradle new file mode 100644 index 0000000..f2ceadb --- /dev/null +++ b/prototype_projects/launcher_v1/build.gradle @@ -0,0 +1,107 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2026.1.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +def ROBOT_MAIN_CLASS = "frc.robot.Main" + +// Define my targets (RoboRIO) and artifacts (deployable files) +// This is added by GradleRIO's backing project DeployUtils. +deploy { + targets { + 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 = 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' + deleteOldFiles = false // Change to true to delete files on roboRIO that no + // longer exist in deploy directory of this project + } + } + } + } +} + +def deployArtifact = deploy.targets.roborio.artifacts.frcJava + +// Set to true to use debug for all targets including JNI, which will drastically impact +// performance. +wpi.java.debugJni = false + +// Set this to true to enable desktop support. +def includeDesktopSupport = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + annotationProcessor wpi.java.deps.wpilibAnnotations() + 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) + + roborioRelease wpi.java.deps.wpilibJniRelease(wpi.platforms.roborio) + roborioRelease wpi.java.vendor.jniRelease(wpi.platforms.roborio) + + nativeDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.desktop) + nativeDebug wpi.java.vendor.jniDebug(wpi.platforms.desktop) + simulationDebug wpi.sim.enableDebug() + + 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:5.10.1' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +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) } } + from('src') { into 'backup/src' } + from('vendordeps') { into 'backup/vendordeps' } + from('build.gradle') { into 'backup' } + 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/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.jar b/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.properties b/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..34bd9ce --- /dev/null +++ b/prototype_projects/launcher_v1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/prototype_projects/launcher_v1/gradlew b/prototype_projects/launcher_v1/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/prototype_projects/launcher_v1/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# 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. +# 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. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# 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/HEAD/platforms/jvm/plugins-application/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 +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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +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 ;; #( + 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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +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" || "$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 + 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 + # 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 +fi + + +# 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"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +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. +# + +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/prototype_projects/launcher_v1/gradlew.bat b/prototype_projects/launcher_v1/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/prototype_projects/launcher_v1/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +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" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +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! +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 + +:omega diff --git a/prototype_projects/launcher_v1/settings.gradle b/prototype_projects/launcher_v1/settings.gradle new file mode 100644 index 0000000..25f6f6e --- /dev/null +++ b/prototype_projects/launcher_v1/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2026' + File frcHome + if (OperatingSystem.current().isWindows()) { + String publicFolder = System.getenv('PUBLIC') + if (publicFolder == null) { + publicFolder = "C:\\Users\\Public" + } + def homeRoot = new File(publicFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } else { + def userFolder = System.getProperty("user.home") + def homeRoot = new File(userFolder, "wpilib") + frcHome = new File(homeRoot, frcYear) + } + def frcHomeMaven = new File(frcHome, 'maven') + maven { + name = 'frcHome' + url = frcHomeMaven + } + } +} + +Properties props = System.getProperties(); +props.setProperty("org.gradle.internal.native.headers.unresolved.dependencies.ignore", "true"); diff --git a/prototype_projects/launcher_v1/src/main/deploy/example.txt b/prototype_projects/launcher_v1/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/deploy/example.txt @@ -0,0 +1,3 @@ +Files placed in this directory will be deployed to the RoboRIO into the +'deploy' directory in the home folder. Use the 'Filesystem.getDeployDirectory' wpilib function +to get a proper path relative to the deploy directory. \ No newline at end of file diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/Constants.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..c50ba05 --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/Constants.java @@ -0,0 +1,19 @@ +// 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 frc.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 class OperatorConstants { + public static final int kDriverControllerPort = 0; + } +} diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/Main.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/Main.java @@ -0,0 +1,25 @@ +// 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 frc.robot; + +import edu.wpi.first.wpilibj.RobotBase; + +/** + * Do NOT add any static variables to this class, or any initialization at all. Unless you know what + * you are doing, do not modify this file except to change the parameter class to the startRobot + * call. + */ +public final class Main { + private Main() {} + + /** + * Main initialization function. Do not perform any initialization here. + * + *

If you change your main robot class, change the parameter type. + */ + public static void main(String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/Robot.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..98f39b7 --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/Robot.java @@ -0,0 +1,110 @@ +// 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 frc.robot; + +import com.revrobotics.spark.SparkMax; +import com.revrobotics.spark.SparkLowLevel.MotorType; + +import edu.wpi.first.wpilibj.TimedRobot; +import edu.wpi.first.wpilibj.motorcontrol.Spark; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.CommandScheduler; + +/** + * The methods in this class are called automatically corresponding to each mode, as described in + * the TimedRobot documentation. If you change the name of this class or the package after creating + * this project, you must also update the Main.java file in the project. + */ +public class Robot extends TimedRobot { + private Command m_autonomousCommand; + + private final RobotContainer m_robotContainer; + private final SparkMax motor = new SparkMax(50, MotorType.kBrushless ); + + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + public Robot() { + // Instantiate our RobotContainer. This will perform all our button bindings, and put our + // autonomous chooser on the dashboard. + m_robotContainer = new RobotContainer(); + + } + + /** + * This function is called every 20 ms, no matter the mode. Use this for items like diagnostics + * that you want ran during disabled, autonomous, teleoperated and test. + * + *

This runs after the mode specific periodic functions, but before LiveWindow and + * SmartDashboard integrated updating. + */ + @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(); + + motor.set(0.8); + } + + /** This function is called once each time the robot enters Disabled mode. */ + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + @Override + public void autonomousInit() { + m_autonomousCommand = m_robotContainer.getAutonomousCommand(); + + // schedule the autonomous command (example) + if (m_autonomousCommand != null) { + CommandScheduler.getInstance().schedule(m_autonomousCommand); + } + } + + /** This function is called periodically during autonomous. */ + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() { + // This makes sure that the autonomous stops running when + // teleop starts running. If you want the autonomous to + // continue until interrupted by another command, remove + // this line or comment it out. + if (m_autonomousCommand != null) { + m_autonomousCommand.cancel(); + } + } + + /** This function is called periodically during operator control. */ + @Override + public void teleopPeriodic() {} + + @Override + public void testInit() { + // Cancels all running commands at the start of test mode. + CommandScheduler.getInstance().cancelAll(); + } + + /** This function is called periodically during test mode. */ + @Override + public void testPeriodic() {} + + /** This function is called once when the robot is first started up. */ + @Override + public void simulationInit() {} + + /** This function is called periodically whilst in simulation. */ + @Override + public void simulationPeriodic() {} +} diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/RobotContainer.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..a33249e --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,63 @@ +// 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 frc.robot; + +import frc.robot.Constants.OperatorConstants; +import frc.robot.commands.Autos; +import frc.robot.commands.ExampleCommand; +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.Trigger; + +/** + * 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 trigger mappings) should be declared here. + */ +public class RobotContainer { + // The robot's subsystems and commands are defined here... + private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); + + // Replace with CommandPS4Controller or CommandJoystick if needed + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + // Configure the trigger bindings + configureBindings(); + } + + /** + * Use this method to define your trigger->command mappings. Triggers can be created via the + * {@link Trigger#Trigger(java.util.function.BooleanSupplier)} constructor with an arbitrary + * predicate, or via the named factories in {@link + * edu.wpi.first.wpilibj2.command.button.CommandGenericHID}'s subclasses for {@link + * CommandXboxController Xbox}/{@link edu.wpi.first.wpilibj2.command.button.CommandPS4Controller + * PS4} controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick Flight + * joysticks}. + */ + private void configureBindings() { + // Schedule `ExampleCommand` when `exampleCondition` changes to `true` + new Trigger(m_exampleSubsystem::exampleCondition) + .onTrue(new ExampleCommand(m_exampleSubsystem)); + + // Schedule `exampleMethodCommand` when the Xbox controller's B button is pressed, + // cancelling on release. + m_driverController.b().whileTrue(m_exampleSubsystem.exampleMethodCommand()); + } + + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ + public Command getAutonomousCommand() { + // An example command will be run in autonomous + return Autos.exampleAuto(m_exampleSubsystem); + } +} diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/Autos.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/Autos.java @@ -0,0 +1,20 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; + +public final class Autos { + /** Example static factory for an autonomous command. */ + public static Command exampleAuto(ExampleSubsystem subsystem) { + return Commands.sequence(subsystem.exampleMethodCommand(), new ExampleCommand(subsystem)); + } + + private Autos() { + throw new UnsupportedOperationException("This is a utility class!"); + } +} diff --git a/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/ExampleCommand.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..3c5b9b4 --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/commands/ExampleCommand.java @@ -0,0 +1,43 @@ +// 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 frc.robot.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj2.command.Command; + +/** An example command that uses an example subsystem. */ +public class ExampleCommand extends Command { + @SuppressWarnings("PMD.UnusedPrivateField") + private final ExampleSubsystem m_subsystem; + + /** + * Creates a new ExampleCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExampleCommand(ExampleSubsystem subsystem) { + m_subsystem = subsystem; + // Use addRequirements() here to declare subsystem dependencies. + addRequirements(subsystem); + } + + // 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() {} + + // 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/prototype_projects/launcher_v1/src/main/java/frc/robot/subsystems/LauncherSubsystem.java b/prototype_projects/launcher_v1/src/main/java/frc/robot/subsystems/LauncherSubsystem.java new file mode 100644 index 0000000..6b375da --- /dev/null +++ b/prototype_projects/launcher_v1/src/main/java/frc/robot/subsystems/LauncherSubsystem.java @@ -0,0 +1,47 @@ +// 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 frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class ExampleSubsystem extends SubsystemBase { + /** Creates a new ExampleSubsystem. */ + public ExampleSubsystem() {} + + /** + * Example command factory method. + * + * @return a command + */ + public Command exampleMethodCommand() { + // Inline construction of command goes here. + // Subsystem::RunOnce implicitly requires `this` subsystem. + return runOnce( + () -> { + /* one-time action goes here */ + }); + } + + /** + * An example method querying a boolean state of the subsystem (for example, a digital sensor). + * + * @return value of some boolean subsystem state, such as a digital sensor. + */ + public boolean exampleCondition() { + // Query some boolean state, such as a digital sensor. + return false; + } + + @Override + public void periodic() { + // This method will be called once per scheduler run + } + + @Override + public void simulationPeriodic() { + // This method will be called once per scheduler run during simulation + } +} diff --git a/prototype_projects/launcher_v1/vendordeps/Phoenix6-26.1.0.json b/prototype_projects/launcher_v1/vendordeps/Phoenix6-26.1.0.json new file mode 100644 index 0000000..dc5dc62 --- /dev/null +++ b/prototype_projects/launcher_v1/vendordeps/Phoenix6-26.1.0.json @@ -0,0 +1,449 @@ +{ + "fileName": "Phoenix6-26.1.0.json", + "name": "CTRE-Phoenix (v6)", + "version": "26.1.0", + "frcYear": "2026", + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2026-latest.json", + "conflictsWith": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-replay-frc2026-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "26.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "26.1.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "26.1.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "26.1.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "26.1.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "26.1.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "26.1.0", + "libName": "CTRE_SimProTalonFXS", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "26.1.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "26.1.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "26.1.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdi", + "version": "26.1.0", + "libName": "CTRE_SimProCANdi", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANdle", + "version": "26.1.0", + "libName": "CTRE_SimProCANdle", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_v1/vendordeps/REVLib.json b/prototype_projects/launcher_v1/vendordeps/REVLib.json new file mode 100644 index 0000000..d35e593 --- /dev/null +++ b/prototype_projects/launcher_v1/vendordeps/REVLib.json @@ -0,0 +1,133 @@ +{ + "fileName": "REVLib.json", + "name": "REVLib", + "version": "2026.0.1", + "frcYear": "2026", + "uuid": "3f48eb8c-50fe-43a6-9cb7-44c86353c4cb", + "mavenUrls": [ + "https://maven.revrobotics.com/" + ], + "jsonUrl": "https://software-metadata.revrobotics.com/REVLib-2026.json", + "javaDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-java", + "version": "2026.0.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.1", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-cpp", + "version": "2026.0.1", + "libName": "REVLib", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "REVLib-driver", + "version": "2026.0.1", + "libName": "REVLibDriver", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibBackendDriver", + "version": "2026.0.1", + "libName": "BackendDriver", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "com.revrobotics.frc", + "artifactId": "RevLibWpiBackendDriver", + "version": "2026.0.1", + "libName": "REVLibWpi", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file diff --git a/prototype_projects/launcher_v1/vendordeps/WPILibNewCommands.json b/prototype_projects/launcher_v1/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..d90630e --- /dev/null +++ b/prototype_projects/launcher_v1/vendordeps/WPILibNewCommands.json @@ -0,0 +1,39 @@ +{ + "fileName": "WPILibNewCommands.json", + "name": "WPILib-New-Commands", + "version": "1.0.0", + "uuid": "111e20f7-815e-48f8-9dd6-e675ce75b266", + "frcYear": "2026", + "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": [ + "linuxsystemcore", + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +}