From e9f407be9d7da142587e554c76589fd0bc7ed3ae Mon Sep 17 00:00:00 2001 From: Stingr Date: Mon, 13 Jan 2025 17:40:55 -0600 Subject: [PATCH 01/32] Moved some projects into the github - Henry --- .../.gradle/7.5.1/checksums/checksums.lock | Bin 17 -> 17 bytes .../.gradle/7.5.1/checksums/md5-checksums.bin | Bin 35247 -> 36397 bytes .../7.5.1/checksums/sha1-checksums.bin | Bin 77513 -> 87557 bytes .../bin/main/frc/components/SwerveDrive.class | Bin 4203 -> 4203 bytes .../components/autonomous/DriveState2.class | Bin 2921 -> 2921 bytes .../components/autonomous/DriveState3.class | Bin 2330 -> 2330 bytes .../main-bot/bin/main/frc/robot/Utility.class | Bin 1149 -> 1149 bytes .../DontOverthinkIt/.gitignore | 178 +++++++ .../.wpilib/wpilib_preferences.json | 6 + .../DontOverthinkIt/WPILib-License.md | 24 + .../DontOverthinkIt/build.gradle | 101 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../DontOverthinkIt/gradlew | 249 +++++++++ .../DontOverthinkIt/gradlew.bat | 92 ++++ .../DontOverthinkIt/settings.gradle | 30 ++ .../src/main/deploy/example.txt | 3 + .../src/main/java/frc/Components/Drive.java | 12 + .../src/main/java/frc/Controllers/Xbox.java | 393 ++++++++++++++ .../src/main/java/frc/robot/Main.java | 25 + .../src/main/java/frc/robot/Robot.java | 64 +++ .../DontOverthinkIt/vendordeps/Phoenix6.json | 339 ++++++++++++ .../vendordeps/WPILibNewCommands.json | 38 ++ .../Henrys_Command_Robot_Test/.gitignore | 178 +++++++ .../.wpilib/wpilib_preferences.json | 6 + .../WPILib-License.md | 24 + .../Henrys_Command_Robot_Test/build.gradle | 101 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../Henrys_Command_Robot_Test/gradlew | 249 +++++++++ .../Henrys_Command_Robot_Test/gradlew.bat | 92 ++++ .../Henrys_Command_Robot_Test/settings.gradle | 30 ++ .../src/main/deploy/example.txt | 3 + .../src/main/java/frc/robot/Constants.java | 19 + .../src/main/java/frc/robot/Main.java | 25 + .../src/main/java/frc/robot/Robot.java | 103 ++++ .../main/java/frc/robot/RobotContainer.java | 63 +++ .../main/java/frc/robot/commands/Autos.java | 20 + .../frc/robot/commands/ExampleCommand.java | 43 ++ .../frc/robot/controllers/LogitechF310.java | 463 ++++++++++++++++ .../main/java/frc/robot/controllers/PS4.java | 498 ++++++++++++++++++ .../main/java/frc/robot/controllers/Xbox.java | 425 +++++++++++++++ .../frc/robot/controllers/package-info.java | 7 + .../robot/subsystems/ExampleSubsystem.java | 48 ++ .../vendordeps/Phoenix5.json | 151 ++++++ .../vendordeps/WPILibNewCommands.json | 38 ++ .../Beginner_Level/Course_Intro.txt | 0 .../Beginner_Level/Java/B1DataTypes.java | 0 .../Beginner_Level/Java/B2Operators.java | 0 .../Java/B3IfElseStatement.java | 0 .../Beginner_Level/Java/B4CheckIn.java | 0 .../Beginner_Level/Java/B5Functions.java | 0 .../Beginner_Level/Java/B6Scope.java | 0 .../B71ClassesAndObjects.java | 0 .../B72BlueprintForHouse.java | 0 .../B73RevisedBlueprintForHouse.java | 0 .../B74AnotherRevisedBlueprint.java | 0 .../Beginner_Level/Java/B8FinalCheckIn.java | 0 .../Beginner_Level/Python/B1DataTypes.py | 0 .../Beginner_Level/Python/B2Operators.py | 0 .../Python/B3IfElseStatement.py | 0 .../Beginner_Level/Python/B4CheckIn.py | 0 .../Beginner_Level/Python/B5Functions.py | 0 .../Beginner_Level/Python/B6Scope.py | 0 .../Python/B7ClassesAndObjects.py | 0 .../Beginner_Level/Python/B8FinalCheckIn.py | 0 .../Intermediate_Level/Intermediate_Intro.txt | 0 .../Java/I0JavaCheatsheet.java | 0 .../Java/I1ConvertingDatatypes.java | 0 .../Intermediate_Level/Java/I2Arrays.java | 0 .../Intermediate_Level/Java/I3Loops.java | 0 .../Intermediate_Level/Java/I4CheckIn.java | 0 .../Intermediate_Level/Java/I5Enums.java | 0 .../Python/I1ConvertingDatatypes.py | 0 .../Intermediate_Level/Python/I2Lists.py | 0 .../Robot_Level/Robot_Intro.txt | 0 2024-2025/Tests/main-bot/.gitignore | 178 +++++++ .../main-bot/.wpilib/wpilib_preferences.json | 6 + 2024-2025/Tests/main-bot/WPILib-License.md | 24 + 2024-2025/Tests/main-bot/build.gradle | 101 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 2024-2025/Tests/main-bot/gradlew | 249 +++++++++ 2024-2025/Tests/main-bot/gradlew.bat | 92 ++++ 2024-2025/Tests/main-bot/settings.gradle | 30 ++ .../main-bot/src/main/deploy/example.txt | 3 + .../src/main/java/frc/robot/Main.java | 25 + .../src/main/java/frc/robot/Robot.java | 72 +++ .../vendordeps/WPILibNewCommands.json | 38 ++ .../Win-Nguyen/Win-Nguyen-2024/.gitignore | 178 +++++++ .../.wpilib/wpilib_preferences.json | 6 + .../Win-Nguyen-2024/WPILib-License.md | 24 + .../Win-Nguyen/Win-Nguyen-2024/build.gradle | 101 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew | 249 +++++++++ .../Win-Nguyen/Win-Nguyen-2024/gradlew.bat | 92 ++++ .../Win-Nguyen-2024/settings.gradle | 30 ++ .../src/main/deploy/example.txt | 3 + .../src/main/deploy/pathplanner/navgrid.json | 1 + .../deploy/pathplanner/paths/newpath.path | 49 ++ .../src/main/deploy/robot_settings.json | 33 ++ .../src/main/java/frc/CommonData.java | 214 ++++++++ .../main/java/frc/components/IComponent.java | 18 + .../main/java/frc/components/SwerveDrive.java | 238 +++++++++ .../main/java/frc/components/SwerveWheel.java | 124 +++++ .../frc/components/autonomous/Autonomous.java | 58 ++ .../java/frc/components/package-info.java | 7 + .../java/frc/controllers/LogitechF310.java | 463 ++++++++++++++++ .../src/main/java/frc/controllers/PS4.java | 498 ++++++++++++++++++ .../src/main/java/frc/controllers/Xbox.java | 425 +++++++++++++++ .../java/frc/controllers/package-info.java | 7 + .../src/main/java/frc/package-info.java | 7 + .../src/main/java/frc/robot/Main.java | 30 ++ .../src/main/java/frc/robot/Robot.java | 179 +++++++ .../src/main/java/frc/robot/Settings.java | 26 + .../src/main/java/frc/robot/Utility.java | 106 ++++ .../src/main/java/frc/robot/package-info.java | 7 + .../main/java/frc/state_machine/State.java | 37 ++ .../java/frc/state_machine/StateMachine.java | 52 ++ .../java/frc/state_machine/package-info.java | 7 + .../Win-Nguyen-2024/vendordeps/NavX.json | 40 ++ .../vendordeps/PathplannerLib.json | 38 ++ .../Win-Nguyen-2024/vendordeps/Phoenix6.json | 339 ++++++++++++ .../vendordeps/WPILibNewCommands.json | 38 ++ 125 files changed, 8710 insertions(+) create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/.wpilib/wpilib_preferences.json create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/WPILib-License.md create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/build.gradle create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew.bat create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/settings.gradle create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/deploy/example.txt create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Components/Drive.java create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Controllers/Xbox.java create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/Phoenix6.json create mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/WPILibNewCommands.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.wpilib/wpilib_preferences.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/WPILib-License.md create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/build.gradle create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew.bat create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/settings.gradle create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/deploy/example.txt create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Constants.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/RobotContainer.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/Autos.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/ExampleCommand.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/LogitechF310.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/PS4.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/Xbox.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/package-info.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/subsystems/ExampleSubsystem.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/Phoenix5.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/WPILibNewCommands.json rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Course_Intro.txt (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B1DataTypes.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B2Operators.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B3IfElseStatement.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B4CheckIn.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B5Functions.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B6Scope.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B7ClassesAndObjects/B71ClassesAndObjects.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B7ClassesAndObjects/B72BlueprintForHouse.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B7ClassesAndObjects/B73RevisedBlueprintForHouse.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B7ClassesAndObjects/B74AnotherRevisedBlueprint.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Java/B8FinalCheckIn.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B1DataTypes.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B2Operators.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B3IfElseStatement.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B4CheckIn.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B5Functions.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B6Scope.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B7ClassesAndObjects.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Beginner_Level/Python/B8FinalCheckIn.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Intermediate_Intro.txt (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I0JavaCheatsheet.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I1ConvertingDatatypes.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I2Arrays.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I3Loops.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I4CheckIn.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Java/I5Enums.java (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Python/I1ConvertingDatatypes.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Intermediate_Level/Python/I2Lists.py (100%) rename {Learning_Materials => 2024-2025/Learning_Materials}/Robot_Level/Robot_Intro.txt (100%) create mode 100644 2024-2025/Tests/main-bot/.gitignore create mode 100644 2024-2025/Tests/main-bot/.wpilib/wpilib_preferences.json create mode 100644 2024-2025/Tests/main-bot/WPILib-License.md create mode 100644 2024-2025/Tests/main-bot/build.gradle create mode 100644 2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar create mode 100644 2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/Tests/main-bot/gradlew create mode 100644 2024-2025/Tests/main-bot/gradlew.bat create mode 100644 2024-2025/Tests/main-bot/settings.gradle create mode 100644 2024-2025/Tests/main-bot/src/main/deploy/example.txt create mode 100644 2024-2025/Tests/main-bot/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/Tests/main-bot/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/Tests/main-bot/vendordeps/WPILibNewCommands.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/.wpilib/wpilib_preferences.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/WPILib-License.md create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/build.gradle create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew.bat create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/settings.gradle create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/example.txt create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/navgrid.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/paths/newpath.path create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/robot_settings.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/CommonData.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/IComponent.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveDrive.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveWheel.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/autonomous/Autonomous.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/package-info.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/LogitechF310.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/PS4.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/Xbox.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/package-info.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/package-info.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Settings.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Utility.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/package-info.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/State.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/StateMachine.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/package-info.java create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/NavX.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/PathplannerLib.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/Phoenix6.json create mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/WPILibNewCommands.json diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/checksums.lock b/2022-2023/main-bot/.gradle/7.5.1/checksums/checksums.lock index 5a04837df2d0b14ec6efcbbc695e4d1019bb6288..985a5eed2a29329f107fa90f728c9aa4c690ac93 100755 GIT binary patch literal 17 VcmZRsSIN6^bXo6A1~6dn1^_oE1rPuL literal 17 VcmZRsSIN6^bXo6A1~6bt1OPVr1myq# diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/md5-checksums.bin b/2022-2023/main-bot/.gradle/7.5.1/checksums/md5-checksums.bin index c8ee3ba224ee25e5ba1f072a12a14e02b07ec53a..ffaa3ce6b2fa9befc716a54fde36ee8db42fd6d5 100755 GIT binary patch delta 1602 zcmX|=eNa?Y7{(SEtUVu>+J7gEFLBBll1$(Gk+p5@T7 zd0IrZiB%ta2Za(L8;w|~y1zZRN8d>wr1 zCi%vbrvREprG z?jBH^*2sy;w^u!QwB+)m{fNoXQuPa3t0gpKC_I%a216;7E5|X-RVn6~SM7^|eG-x~ zsaKth_IwG(msrw{cQR>G6%Bi4v1gk44#HodDs&hzduTWGM;v^aDF;y!Hq_BP;fZw; z`t~eW8C#iagd`eAX_FPGp~L0#JK&`n&EY0H&g4s}IlVY;5FJeWDl z#ZbKjWxB$T+(_gJwiX|ty&~LmN0BG+ZG{ANE<67^^tp6Glz?;T5}Fy0(!k1i^hP+j zGC;_qUiDfGR#T8kV9JKjNC8L|l3g{iN)M52u8Q^4GjeOeMIZ8Nu9@Q~dDSz_b+-z&Mymcz z#SfHuerhEK3HrTs$T}AiizO89WYrk$IeG7AdR#fL#;DW!Hg&4Vm2amHyzVA`Et3#m zO1ZKD-Mc7PPoeKM2~Ye#xq(#kjSBhqQb!mJX=^ZS^h8{9I$SYIXf=5@<(lhfn-V_3 zgweAp*W4}mndnQ*eSid$ghA$gzlk+nWDz%=cawo7^+Zqv*qQI8QhdfW_{vJCj=>+#ibTPH~ wCmc-O`x4=izVu>b%7 delta 892 zcmX|;T}YEr9LD!fCtftPW-^b#(wu#+G|V}oi(MG&i%nBgM-7{0_>n|@mP<3Wz_iAL z5z#;|B0^-zp>CEeoA1RSTipa|DMAGiF&7wl6P@>*m)@)2^ZcLxb2#srdaMmX`@F~~ zIdRHZfH7ksmW{9J%dAw1ek3M|>bE^hqVG%vNi=hZRiYR2wo3?@{WzL$L`QxcV>=yG zzbjo!5ACsQ>F7RJGW}f;PNMTg(-M79?2_=;+C@i8?2^{hJ)QF~o!T8gQguVQ{h>t^ zmRS^<(O|fZz*lBaRQM^v$M+}a_P<(3f$fuFRxOq}(kCZYX*xkhsja0+lMG$Nhss?2hBJO63 z1c3_SyZKH`Rg+k)Bp6W}WMox}owx8Y-+4{!EHNeg6$Jm&s-QU?^9U?eJUb_&)k)w{ z)mL;@@y)rrm~A4^H;dFAOt92Xr*dyL(x2fWc?%Lff(*E#df5o~5SUyUn{xYQw}yWy zfWO(S{2=)pLd^s@)x1|8fvs8;Ok&fhq2dq)dvxw` z*h}4Fw-2uAkj9?vsu-W&fES`12p zDZ7W@Y=clfK8vZdK7Lx0s(N`cFeH+FNO*bkLLXKx5*(=){vakUaewg-f|m*Qisq$E O_?X)0HPCPM(Y$|;(qR7p diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/sha1-checksums.bin b/2022-2023/main-bot/.gradle/7.5.1/checksums/sha1-checksums.bin index 2d75895c4fb3bef19f9409189f814825d0627f19..ee365d5e8c6e55f14e7441618254d7464af00930 100755 GIT binary patch delta 6716 zcmY+I3tUWT8^D{?`jVWnMLBJn>7x5(x-e5s(^Z#I&8R4(C8d_!ZEhjSElD1A#D;|2 z+GNDKBz9dkHfc$$?XyWs*;Gh3={s|tGktb`zka{x`M=NYy`J~2szD>;jOJMv1I{6E zH3;Nxy!XmA;L-O-)s#Pua}lgG;8||6bsDklyzvTz;$H^Y-=_G4J^O;c>p&h^16c;V zsPy>apa9R$yATFdsf^QiYJ1AgG|faFMhx*R=OUM&2g{HPwhr-}Hm{{y2Q83KPJ&|w ze8N?$((upL-;f1fQB)XMafsNoGJ{2m&j|g^pU~q9&d88uQO%6??5tb_0AC!`VjcPp2)APi512IC|Pr%h)Ty?uKNnRvQth>p%o(jENnm?LuM zry%Buh~U!Itu<$yk-2L^GEYwEop{qo_NO6}ZHIE+K5lW5rDa;ZXC}hXN#M_y6Y=k* zx_^&mCsgVSS^P9&_;*orFK&KhC}0%Z|9b z8pNi2;`ZC68E>|Qp`F75h%_xD3O5OKXIc9r3wDJ8(?ssM>(@Fqooxw5C^-o+WJN_1thc=Lqw!R!P%bD+dr z$i4ddT5;3c)IZR8>M=!wIlTsFoihsva|uK^xef*GQ`Jcf=!;mMXd|TR23d%i^fDAAJxyPl7sY9=E3X zx1?XE9cSk<;s&(Q@Im>~twCnp?r7;0tI)I=%^~u2Joa{N3q|PO06MlpuezvoA!WAy z212iks!Hn?ZvH0Wk2CDn5M`?PWA&_IF#G+xKGQX_3FOYTaM6w zCRE$@5{a9R?x-2_4Kg=7MTH&Rx#+%`Mkghg5IU}gTlQ(w$FCC@dhQECCe=}8`g`i9 z-_I^QDn>rVaEM=>=wN;;dN^{|$|3&HEUj}eRf;^o8^jJGVpZ|>Z@YI+LMG0FWCtPl zb=Si+4_0^`M(87e0tXqlwZ)aQzAXpWHYcJRnjI3k|9m_%@hYcyJ6eVZfWKoR_w55o zm%?m`IYM`LIObSJa9mGqT`llLHc12_0v@;XQqH(^lVL~f5_ zjP`FQ%pW22jE7_=nOEP(pBAqybM6YyV66G zq1;JMTpd|+qD6lW#+Y;r2AmQt2i#8^hWm2L5QbQ(0u1a5bk)&+@IA|8A;y_c{1tIU za432sGEpR~bWS2-e=0~EH}xelVF1+8?8JrTK1+)okomQPs|%k{gx-%fcUg%nvI{9spkoxCY%H!Ltm*G*TK?=`AUhe4Gj?j#^MIV{vc5JMK01%s`eVN;W!C3$MqGj zqkkU3*y=&)Jb{ifS}l`D&8QWg$5}DnLeQBLdCJgQN>OwE5%rhrxk1ugTO0jg*B-@^LxvcM4!hiUyoYNmP>V z5NJ%1L3^&gV*RSI3po_zzFbXIj9>nwU?O|uyXAvsbE2iztXD$IuRmYBF+cPs3xn%b73uoO zESn@5acJzJtEhNIPwI)f0<*M}RxI2AU9UxichzjG8@vi+E^EQOg-^Uqx&Cxi0D(+k z2;vrD)VPGRQm?-?v=Dk8P-Pki*LsevD_{eNx`#MrBmDHVi9OG!E>>~U@4y+LIDIgr&V zCua8FjIHfuKctf9P}iDI{C;@&`gvPMqFq1}#J}0d<#)zshWPH9htRzk8s6}U1(L@l zV?MLrH~T?|naJasRJNgzK2vOKtsQ#zN(rSw7pPf%v{f^LO=-z6zc=>S6#!G9E`{_ePTNQEh zKZ2j^o7nmHZG_afX~ds_ook}A+3E%Fp+LwdMDr`UJ0sX3l2f45o=@y3KXSfsSCW=q;POi@lHq7pgkxyl3oz zuDERW${QI6SsnYhuA#5m(=REi&^SOFj53fxD6nSOaefBZo=nJVIWL*CT z8q>n#Z&|ac-oUL7NksbAcYQ|j>@p84gA%a_W_KA7i%PBZ*LFFht;`6LyOM~j`=)4w z3zLx9S3xz+O3Bb}O6>}=zzneaD6^EFm42=-x^n_y=u=hyrSFS^o~PZB zBNX?lN{yCmml^V{e?dO6dWa`bFD_VKX3q+5Q)uwYC!(ME_(eAKqpeQ?RPdTc&Er1m9p=Ujv9K{n~^&CGo%H1zQ;igacLKdokO1fd*N zfVqmkdEJ$DjTpee73_ROglu=leQ8jwx2fia(wi0=g%l^4z>BXg*Sm7n%;ziTx(dSUcVGuF8U zZuxGsTMN(w?B@ZfA&G*aE2oNd&KR`!aP+)z0jKhJi+lC0TD; zww~j#nT9suvTY&xih@eEtCOBVjK7?)GSI3InYsjR9d%&fvz*vF>vCI3i+a%AD~v$9JHutg<|nr5`PfD{;@nKX`j$L)u2kxdm*%^Z%8`UFuK z|B2ScLOqTp2`O=-l%M-AKK|J$jK+NUN$5w7`RE%$aMoC*WsfU3WGSyLD3Q>xlkw^C z2l)XKHe{i;AH=)R>MShZrM$X4?!g7A#yJ8{vy@kt$33s1mW`#9Na)?gc=wKh=jhEy z|Bgz6LpE9+1Zg%2;@zmd2GiY4o?bl9)iid(_G~6k?pwlF^jB?%O;(sg(^Bx&toT0gad^)4y0~eri z&%ZUsexM)pfssD*AcU11Abm)>#7cXh{R@c%$zCSzAOSP?GX8^`A?FK;ghtw8agDvU z9}MgzVIWU`B&}&hFl$634DRo>rl+M|%|z9#ym6{cV4I(VX0DY^MnU%!D)Qm(T6dbLk* zD3=8DJY~1)IkDiKM=I~0jJ6T9X)T9tqbmwM0VT9HGf!VxUnqWi)ic{+I>l5h#{*)G z`5NF;R!R@q2neIlgO06!i>X5Cp=?)@=X5)B<5tJBQRzEphA!;MQ*MKL^*AP@Xev|9 zH)o+Dn1qsj`pQ0i^A!6uRtAlmm}ui4LD~uuPOoOHX{{`s1mhhlO-}o2v2Bl;Halip z){lR}T;VG=q2((m3?yMO<}{uMb23OcL@{l~32=q(Pske7Qkf`2Zvv3VfB6ErF_nbD z{fs5g5ja!mS|U)xw@PffN>f%nI1e@Eok7)%=b#$Nbg5K|n(_%lKSJ`E0r=vPQX0Vw z0Q1-(AuOM<<}05wBxGVzexUN?pc;%ZFuxs^?jXS~k_p9shhwRA87?JaD15pMJ!zV{xtf*6RF}#t#}%0zcM}*Q&gI= zZTl9~su;fw(;3ZlC)7?S!Ke_mJuv4m3FT~C9@FN37~3eijagEr2e4BFAN-@NluO1G zFTiRvxpWZ8k7%P)2;PNE$ISv^aUoN=*+S;5t;C#WyP+_Sgaz?T7PD#?eT3dLYZ=Xa zE_kn{Hw`We^QE9oYlqfh+bSyCltb?!)^rz?tYyNRABQHCCRoF`bqt$O1!3!$;w|tF z_Eps$s?vnRRAyB`wVBY778fyrtqLKth(6e6p>`Zj&mtk3)y`09N>fu-y#SI~B;{a& zb8mGWQjd_7*p$OezBS&J)NauK_d3R8vW^1xV4!!-W9m^RzwIapi6Nmh0kyGkFM)aZDtEE11sEkU{j=Q+HAhKGYKDc_ZiCKsBs8;5 zSAjF1o?{sk!tNS0&^2TpQ)$NA=zv>My8%n-Hiu|NV_))i$5FF@v}opCuS{(p32n3{ zp276QJ_=`BZxeJyGd;DB27WP9n*DLeo}rRP%jqxC42<^@`jI{kKztJ2u2Ah0=$Xno$uv0H!VS8g zTC-6Lg6C-Fm`V*;(9zQ^F}hPBb;uGoq|0!sP9^?AXB()aWq}PWKCve Ls1H|u)SUhYnC=sF delta 930 zcmYk4Uuct87{)zWbe+%&W6L?8)qJ*UY^U)%aptpsY})Qm^M%n}tk&U$O=PuJaKd0T z*}#H>gx$opKEzKiROVQ;;JS^E)SI=V1Itv3Ft(0?7BAMW)b=y|lW1FszK9oIJUsB8 z_nepWd*1l6-BZ^-KKK456mIFg#MhO{?m zzNG6u0~w!$caOl~J@Zz_yG0C%y)m@;CDMKkbAAKOdIUE~b^H2J*vH|mQqWdKXXNf` z1Lvy4D6TPZu_lBW*hg956b8Gt0@9Xhp zyK2tWQ+J?U#j|#fA}Z7csPN}!s(JZC#;tFTe(S(r$5it-g$Xov`q9v-;!Y>W^-i6n zV(bdH#PPvmobS@%jGjPER7aZp9{IhfibpAKb;n#MSS?cjeFe{C7@5f|hNebv?th61 zl4qK}H?3Kj>1u{`GZG)q2z1YAD1I10{9!M=vp#gq8W^ALMfY43#kPWg&GES{F=n5o zJZ#Eh1SjWXFEZG@Kx?!>d|zbHNBnz;y$`M&e}(@)#QtAv@G-5u{Uk`hj}UBwU`Jkp z4ie~o==BB$o|5Xb*j)%?tU+L?ATZf5g8Z}I)F2CE^Lu@!?qFBVoP!-PPrk-z-uwNg y?PStA^T7jVcD+trnI>tTDq_AHqeYAs%MJ$3AJ(&S$L0&al*Qh>@!eajV&ik3u&O5j diff --git a/2022-2023/main-bot/bin/main/frc/components/SwerveDrive.class b/2022-2023/main-bot/bin/main/frc/components/SwerveDrive.class index b0bfedfe73f76a3d869a8316915d176675da1630..084cf57a147d53f65536a6aa4b8ea97552898ae3 100644 GIT binary patch delta 978 zcmYjOOH30{6g{ugb~^R7h48UOkgUxulpjSqmi-N z;6#w34>kgOG;%94(KSf%L1OFMs# zb%8R4wn_0?xS|7*=u|Ko^4~FxkO*v1yb%Fqd?;k>t1G{h1k?RA9N{(60sMvyCVtLo z8_3X{Yf!Z617F~oFmgIb>?)RUC!arakUg7D? z3c3Vj@0}@!VoWRkh(`_4Vzch0%%+Pe1gHeq9=Iwyz1 zgIofs(BGzjU=M`~hm5@h+003@9SXcCkde=R5wEl#KBifUT^jtNPABjdHnQI&FRdz7 zUQCgW3>hU8E)RQRk?o}@mr=&1Ed^2qD%n>{xg&ssqWBPVOL|i&j!+y$C4r;9H8Nr* zhD`;fsHQlUBx@W0lP4%@Qslpz1ho`t*de2ipn`pp^H~Q`?K0{K{H(_6cRCS31I1~a zVH;A2hS4a@n+Sr1?3*Kl{jn>o)0$4svAfpE+IDox=pb-+C%S^+cz=Iqb#5F`^T<2GBMTeuozUV+tuqis!%MRLnq@7*0O%ZLuZm%XJ%);__ z5?Js#+(g&#jN&=&@p*g9Eh!CC6fXoRbFwww*QcPJT~ji-(5Rwa4q%RAUObZ=GrP@! zLB6AQEK;;#+Q}bnSwy*ldqweDbV|G9iIHd`7P(`ZF_BoMcq0V|G-Dq7R#3QvB3*uqfFZ5&U(q6hD>x7_tefa?64^FU zRT9}Z(Q$#65aMcKgKL~LBz+MfseBoyB4vhE0BaH$HWAxWv0cRWWW0j2OK6_sy;;!0 z=4gd^p2xdV-J%r{j(Fd=fQx(OESj8DPJ zNA2dh-tDW?fmNg}rxGsiKz#$ihYR!gjhJ zXj}dhQ1}PTq!gZxFTfBXd*{yBos{Hjp2y51cYW}n|9wo$~&R1dd1 eqY67y?bv2FleAk?$5k&r;A2fD*6v(j0}8PiDilUIf;4c`o4)J8Jl-7PGV$J PRV&}YpuRbPS(gz2{NoUa diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore b/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore new file mode 100644 index 0000000..cdbfbfa --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore @@ -0,0 +1,178 @@ +# 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 +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/.wpilib/wpilib_preferences.json b/2024-2025/FreshmanProjectBots/DontOverthinkIt/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a5ebb07 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2024", + "teamNumber": 5098 +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/WPILib-License.md b/2024-2025/FreshmanProjectBots/DontOverthinkIt/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/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/2024-2025/FreshmanProjectBots/DontOverthinkIt/build.gradle b/2024-2025/FreshmanProjectBots/DontOverthinkIt/build.gradle new file mode 100644 index 0000000..1e8da00 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/build.gradle @@ -0,0 +1,101 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2024.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' + } + } + } + } +} + +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 = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + 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/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.properties b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7015f6b --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew @@ -0,0 +1,249 @@ +#!/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. +# + +############################################################################## +# +# 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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || 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/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew.bat b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@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. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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/2024-2025/FreshmanProjectBots/DontOverthinkIt/settings.gradle b/2024-2025/FreshmanProjectBots/DontOverthinkIt/settings.gradle new file mode 100644 index 0000000..3e30f84 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2024' + 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/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/deploy/example.txt b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/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/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Components/Drive.java b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Components/Drive.java new file mode 100644 index 0000000..946bd8a --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Components/Drive.java @@ -0,0 +1,12 @@ +package frc.Components; +import com.ctre.phoenix6.hardware.TalonFX; +// import com.ctre.phoenix6.hardware.TalonSRX; +// import com.ctre.phoenix6.motorcontrol.ControlMode; +// import com.ctre.phoenix6.motorcontrol.can.TalonSRX; + +public class Drive { + private TalonFX motor; + + public Drive(int motorId) { motor = new TalonFX(motorId); } + public void MoveMotor(double power) { TalonFX leftMotor1 = new TalonFX(1); } +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Controllers/Xbox.java b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Controllers/Xbox.java new file mode 100644 index 0000000..d8462f2 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/Controllers/Xbox.java @@ -0,0 +1,393 @@ +package frc.Controllers; + +import edu.wpi.first.wpilibj.GenericHID.RumbleType; +import edu.wpi.first.wpilibj.XboxController; + +/** + * This class is used to create an Xbox controller object. + *

+ * This class uses the {@code XboxController} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see PS4 + * + * @see edu.wpi.first.wpilibj.XboxController + */ +public class Xbox { + + /** + * The controller object. + * In this case, a {@code XboxController} object. + */ + private XboxController controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the Xbox class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public Xbox(final int id, final double deadBandValue) { + controller = new XboxController(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftY + */ + public double getLeftX() { return deadBand(controller.getLeftX()); } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftX + */ + public double getLeftY() { return deadBand(controller.getLeftY()); } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see Xbox#getRightY + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightX() { return deadBand(controller.getRightX()); } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see Xbox#getRightX + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightY() { return deadBand(controller.getRightY()); } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see Xbox#getRightTrigger + */ + public double getLeftTrigger() { return controller.getLeftTriggerAxis(); } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see Xbox#getLeftTrigger + */ + public double getRightTrigger() { return controller.getRightTriggerAxis(); } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return value of the A button + * + * @see Xbox#getBButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getAButton() { return controller.getAButton(); } + + /** + * Method for returning the value of the B button. + * + * @return value of the B button + * + * @see Xbox#getAButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getBButton() { return controller.getBButton(); } + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getYButton + */ + public boolean getXButton() { return controller.getXButton(); } + + /** + * Method for returning the value of the Y button. + * + * @return value of the Y button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getXButton + */ + public boolean getYButton() { return controller.getYButton(); } + + /** + * Method for returning the value of the start button. + * + * @return value of the start button + * + * @see Xbox#getBackButton + */ + public boolean getStartButton() { return controller.getStartButton(); } + + /** + * Method for returning the value of the back button. + * + * @return value of the back button + * + * @see Xbox#getStartButton + */ + public boolean getBackButton() { return controller.getBackButton(); } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see Xbox#getRightBumper + */ + public boolean getLeftBumper() { return controller.getLeftBumper(); } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see Xbox#getLeftBumper + */ + public boolean getRightBumper() { return controller.getRightBumper(); } + + /** + * Method for returning the value of the left stick button. + * + * @return value of the left stick button + * + * @see Xbox#getRightStickButton + */ + public boolean getLeftStickButton() { + return controller.getLeftStickButton(); + } + + /** + * Method for returning the value of the right stick button. + * + * @return value of the right stick button + * + * @see Xbox#getLeftStickButton + */ + public boolean getRightStickButton() { + return controller.getRightStickButton(); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see Xbox.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { return (controller.isConnected()); } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { return controller.getName(); } +} diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Main.java b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/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/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Robot.java b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..e8946ec --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/src/main/java/frc/robot/Robot.java @@ -0,0 +1,64 @@ +// 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 frc.Components.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 { + /** + * This function is run when the robot is first started up and should be used + * for any initialization code. + */ + + Drive drive; + + @Override + public void robotInit() { + drive = new Drive(0); // Heyyyy bestie this is a temporary ID + } + + @Override + public void robotPeriodic() {} + + @Override + public void autonomousInit() {} + + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() {} + + @Override + public void teleopPeriodic() {} + + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + @Override + public void testInit() {} + + @Override + public void testPeriodic() { + drive.MoveMotor(0.5); + } + + @Override + public void simulationInit() {} + + @Override + public void simulationPeriodic() {} +} diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/Phoenix6.json b/2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/Phoenix6.json new file mode 100644 index 0000000..0322385 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/Phoenix6.json @@ -0,0 +1,339 @@ +{ + "fileName": "Phoenix6.json", + "name": "CTRE-Phoenix (v6)", + "version": "24.3.0", + "frcYear": 2024, + "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-frc2024-latest.json", + "conflictsWith": [ + { + "uuid": "3fcf3402-e646-4fa6-971e-18afe8173b1a", + "errorMessage": "The combined Phoenix-6-And-5 vendordep is no longer supported. Please remove the vendordep and instead add both the latest Phoenix 6 vendordep and Phoenix 5 vendordep.", + "offlineFileName": "Phoenix6And5.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "24.3.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "24.3.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "24.3.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "24.3.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "24.3.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "24.3.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "24.3.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "24.3.0", + "libName": "CTRE_SimTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "24.3.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "24.3.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "24.3.0", + "libName": "CTRE_SimCANCoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "24.3.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "24.3.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "24.3.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/WPILibNewCommands.json b/2024-2025/FreshmanProjectBots/DontOverthinkIt/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..67bf389 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/DontOverthinkIt/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": "2024", + "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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore new file mode 100644 index 0000000..cdbfbfa --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore @@ -0,0 +1,178 @@ +# 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 +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.wpilib/wpilib_preferences.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a5ebb07 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2024", + "teamNumber": 5098 +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/WPILib-License.md b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/build.gradle b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/build.gradle new file mode 100644 index 0000000..1e8da00 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/build.gradle @@ -0,0 +1,101 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2024.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' + } + } + } + } +} + +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 = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.properties b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7015f6b --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew @@ -0,0 +1,249 @@ +#!/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. +# + +############################################################################## +# +# 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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew.bat b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@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. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/settings.gradle b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/settings.gradle new file mode 100644 index 0000000..3e30f84 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2024' + 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/deploy/example.txt b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Constants.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..c50ba05 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Main.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Robot.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..687a0a0 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/RobotContainer.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..a33249e --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/Autos.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/ExampleCommand.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..7481d3c --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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"}) + 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/LogitechF310.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/LogitechF310.java new file mode 100644 index 0000000..74a8997 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/LogitechF310.java @@ -0,0 +1,463 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.Joystick; + +/** + * This class is used to create an LogitechF310 controller object. + *

+ * This class uses the {@code Joystick} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see PS4 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.Joystick + */ +public class LogitechF310 { + + // Button IDs // + + /** + * The axis ID for the left joystick x. + */ + private static final int JOYSTICK_LEFT_X = 0; + + /** + * The axis ID for the left joystick y. + */ + private static final int JOYSTICK_LEFT_Y = 1; + + /** + * The axis ID for the left trigger. + */ + private static final int TRIGGER_LEFT = 2; + + /** + * The axis ID for the right trigger. + */ + private static final int TRIGGER_RIGHT = 3; + + /** + * The axis ID for the right joystick x. + */ + private static final int JOYSTICK_RIGHT_X = 4; + + /** + * The axis ID for the right joystick y. + */ + private static final int JOYSTICK_RIGHT_Y = 5; + + /** + * The button ID for the A button. + */ + private static final int BUTTON_A = 1; + + /** + * The button ID for the B button. + */ + private static final int BUTTON_B = 2; + + /** + * The button ID for the X button. + */ + private static final int BUTTON_X = 3; + + /** + * The button ID for the Y button. + */ + private static final int BUTTON_Y = 4; + + /** + * The button ID for the left bumper. + */ + private static final int BUTTON_LEFT_BUMPER = 5; + + /** + * The button ID for the right bumper. + */ + private static final int BUTTON_RIGHT_BUMPER = 6; + + /** + * The button ID for the start button. + */ + private static final int BUTTON_START = 7; + + /** + * The button ID for the back button. + */ + private static final int BUTTON_BACK = 8; + + /** + * The controller object. + * In this case, a {@code Joystick} object. + * + * @see edu.wpi.first.wpilibj.Joystick + */ + private Joystick controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the LogitechF310 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public LogitechF310(final int id, final double deadBandValue) { + controller = new Joystick(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_X)); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_Y)); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_X)); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_Y)); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return Value of the left trigger + * + * @see LogitechF310#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_LEFT)); + } + + /** + * Method for returning the value of the right trigger. + * + * @return Value of the right trigger + * + * @see LogitechF310#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_RIGHT)); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return Value of the A button + * + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getAButton() { + return controller.getRawButton(BUTTON_A); + } + + /** + * Method for returning the value of the B button. + * + * @return Value of the B button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getBButton() { + return controller.getRawButton(BUTTON_B); + } + + /** + * Method for returning the value of the X button. + * + * @return Value of the X button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getYButton + */ + public boolean getXButton() { + return controller.getRawButton(BUTTON_X); + } + + /** + * Method for returning the value of the Y button. + * + * @return Value of the Y button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + */ + public boolean getYButton() { + return controller.getRawButton(BUTTON_Y); + } + + /** + * Method for returning the value of the back button. + * + * @return Value of the back button + * + * @see LogitechF310#getStartButton + */ + public boolean getBackButton() { + return controller.getRawButton(BUTTON_START); + } + + /** + * Method for returning the value of the start button. + * + * @return Value of the start button + * + * @see LogitechF310#getBackButton + */ + public boolean getStartButton() { + return controller.getRawButton(BUTTON_BACK); + } + + /** + * Method for returning the value of the left bumper. + * + * @return Value of the left bumper + * + * @see LogitechF310#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getRawButton(BUTTON_LEFT_BUMPER); + } + + /** + * Method for returning the value of the right bumper. + * + * @return Value of the right bumper + * + * @see LogitechF310#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRawButton(BUTTON_RIGHT_BUMPER); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see LogitechF310.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + // This controller does not have rumble functionality :( + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/PS4.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/PS4.java new file mode 100644 index 0000000..cef2652 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/PS4.java @@ -0,0 +1,498 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.PS4Controller; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an PS4 controller object. + *

+ * This class uses the {@code PS4Controller} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ +public class PS4 { + + /** + * The controller object. + * In this case, a {@code PS4Controller} object. + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ + private PS4Controller controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the PS4 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public PS4(final int id, final double deadBandValue) { + controller = new PS4Controller(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see PS4#getLeftX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see PS4#getRightX + * @see PS4#getLeftX + * @see PS4#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see PS4#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getL2Axis()); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see PS4#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getR2Axis()); + } + + // Button Methods // + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getXButton() { + return controller.getCrossButton(); + } + + /** + * Method for returning the value of the Square button. + * + * @return value of the Square button + * + * @see PS4#getXButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getSquareButton() { + return controller.getSquareButton(); + } + + /** + * Method for returning the value of the Circle button. + * + * @return value of the Circle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getCircleButton() { + return controller.getCircleButton(); + } + + /** + * Method for returning the value of the Triangle button. + * + * @return value of the Triangle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getTriangleButton() { + return controller.getTriangleButton(); + } + + /** + * Method for returning the value of the Share button. + * + * @return value of the Share button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getShareButton() { + return controller.getShareButton(); + } + + /** + * Method for returning the value of the Options button. + * + * @return value of the Options button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getOptionsButton() { + return controller.getOptionsButton(); + } + + /** + * Method for returning the value of the PlayStation button. + * + * @return value of the PlayStation button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getTouchpadButton + */ + public boolean getPlayStationButton() { + return controller.getPSButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see PS4#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getL1Button(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see PS4#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getR1Button(); + } + + /** + * Method for returning the value of the left joystick button. + * + * @return value of the left joystick button + * + * @see PS4#getRightJoystickButton + */ + public boolean getLeftJoystickButton() { + return controller.getL3Button(); + } + + /** + * Method for returning the value of the right joystick button. + * + * @return value of the right joystick button + * + * @see PS4#getLeftJoystickButton + */ + public boolean getRightJoystickButton() { + return controller.getR3Button(); + } + + // Touchpad Methods // + + /** + * Method for returning the value of the Touchpad button. + * + * @return value of the Touchpad button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + */ + public boolean getTouchpadButton() { + return controller.getTouchpad(); + } + + // There is a 'touchpad' that uses an event loop + // but for the purpose of this class, it is not needed + // It might be added in a future version if there becomes a need for it + // If you have a need for it or have added it yourself, + // Submit a pull request or submit an issue on the GitHub repository + // https://github.com/J-The-Fox/FRC-Team-5098 + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see PS4.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/Xbox.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/Xbox.java new file mode 100644 index 0000000..c8cc131 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/Xbox.java @@ -0,0 +1,425 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an Xbox controller object. + *

+ * This class uses the {@code XboxController} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see PS4 + * + * @see edu.wpi.first.wpilibj.XboxController + */ +public class Xbox { + + /** + * The controller object. + * In this case, a {@code XboxController} object. + */ + private XboxController controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the Xbox class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public Xbox(final int id, final double deadBandValue) { + controller = new XboxController(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see Xbox#getRightY + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see Xbox#getRightX + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see Xbox#getRightTrigger + */ + public double getLeftTrigger() { + return controller.getLeftTriggerAxis(); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see Xbox#getLeftTrigger + */ + public double getRightTrigger() { + return controller.getRightTriggerAxis(); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return value of the A button + * + * @see Xbox#getBButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getAButton() { + return controller.getAButton(); + } + + /** + * Method for returning the value of the B button. + * + * @return value of the B button + * + * @see Xbox#getAButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getBButton() { + return controller.getBButton(); + } + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getYButton + */ + public boolean getXButton() { + return controller.getXButton(); + } + + /** + * Method for returning the value of the Y button. + * + * @return value of the Y button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getXButton + */ + public boolean getYButton() { + return controller.getYButton(); + } + + /** + * Method for returning the value of the start button. + * + * @return value of the start button + * + * @see Xbox#getBackButton + */ + public boolean getStartButton() { + return controller.getStartButton(); + } + + /** + * Method for returning the value of the back button. + * + * @return value of the back button + * + * @see Xbox#getStartButton + */ + public boolean getBackButton() { + return controller.getBackButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see Xbox#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getLeftBumper(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see Xbox#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRightBumper(); + } + + /** + * Method for returning the value of the left stick button. + * + * @return value of the left stick button + * + * @see Xbox#getRightStickButton + */ + public boolean getLeftStickButton() { + return controller.getLeftStickButton(); + } + + /** + * Method for returning the value of the right stick button. + * + * @return value of the right stick button + * + * @see Xbox#getLeftStickButton + */ + public boolean getRightStickButton() { + return controller.getRightStickButton(); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see Xbox.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/package-info.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/package-info.java new file mode 100644 index 0000000..80793a8 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/controllers/package-info.java @@ -0,0 +1,7 @@ +/** + * Holds controller classes + *

+ * This can be used to hold both custom and pre-made controller classes. + *

+ */ +package frc.controllers; diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..39c2a44 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/src/main/java/frc/robot/subsystems/ExampleSubsystem.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.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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/Phoenix5.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/Phoenix5.json new file mode 100644 index 0000000..d0fade6 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/Phoenix5.json @@ -0,0 +1,151 @@ +{ + "fileName": "Phoenix5.json", + "name": "CTRE-Phoenix (v5)", + "version": "5.32.0-beta-5", + "frcYear": 2024, + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-frc2024-beta-latest.json", + "requires": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Phoenix 5 requires low-level libraries from Phoenix 6. Please add the Phoenix 6 vendordep before adding Phoenix 5.", + "offlineFileName": "Phoenix6.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-frc2024-beta-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.32.0-beta-5" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.32.0-beta-5" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.32.0-beta-5", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.32.0-beta-5", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.32.0-beta-5", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.32.0-beta-5", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.32.0-beta-5", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "5.32.0-beta-5", + "libName": "CTRE_Phoenix_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "api-cpp-sim", + "version": "5.32.0-beta-5", + "libName": "CTRE_PhoenixSim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.32.0-beta-5", + "libName": "CTRE_PhoenixCCISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/WPILibNewCommands.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..67bf389 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/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": "2024", + "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/Learning_Materials/Beginner_Level/Course_Intro.txt b/2024-2025/Learning_Materials/Beginner_Level/Course_Intro.txt similarity index 100% rename from Learning_Materials/Beginner_Level/Course_Intro.txt rename to 2024-2025/Learning_Materials/Beginner_Level/Course_Intro.txt diff --git a/Learning_Materials/Beginner_Level/Java/B1DataTypes.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B1DataTypes.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B1DataTypes.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B1DataTypes.java diff --git a/Learning_Materials/Beginner_Level/Java/B2Operators.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B2Operators.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B2Operators.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B2Operators.java diff --git a/Learning_Materials/Beginner_Level/Java/B3IfElseStatement.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B3IfElseStatement.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B3IfElseStatement.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B3IfElseStatement.java diff --git a/Learning_Materials/Beginner_Level/Java/B4CheckIn.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B4CheckIn.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B4CheckIn.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B4CheckIn.java diff --git a/Learning_Materials/Beginner_Level/Java/B5Functions.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B5Functions.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B5Functions.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B5Functions.java diff --git a/Learning_Materials/Beginner_Level/Java/B6Scope.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B6Scope.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B6Scope.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B6Scope.java diff --git a/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B71ClassesAndObjects.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B71ClassesAndObjects.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B71ClassesAndObjects.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B71ClassesAndObjects.java diff --git a/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B72BlueprintForHouse.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B72BlueprintForHouse.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B72BlueprintForHouse.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B72BlueprintForHouse.java diff --git a/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B73RevisedBlueprintForHouse.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B73RevisedBlueprintForHouse.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B73RevisedBlueprintForHouse.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B73RevisedBlueprintForHouse.java diff --git a/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B74AnotherRevisedBlueprint.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B74AnotherRevisedBlueprint.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B74AnotherRevisedBlueprint.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B7ClassesAndObjects/B74AnotherRevisedBlueprint.java diff --git a/Learning_Materials/Beginner_Level/Java/B8FinalCheckIn.java b/2024-2025/Learning_Materials/Beginner_Level/Java/B8FinalCheckIn.java similarity index 100% rename from Learning_Materials/Beginner_Level/Java/B8FinalCheckIn.java rename to 2024-2025/Learning_Materials/Beginner_Level/Java/B8FinalCheckIn.java diff --git a/Learning_Materials/Beginner_Level/Python/B1DataTypes.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B1DataTypes.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B1DataTypes.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B1DataTypes.py diff --git a/Learning_Materials/Beginner_Level/Python/B2Operators.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B2Operators.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B2Operators.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B2Operators.py diff --git a/Learning_Materials/Beginner_Level/Python/B3IfElseStatement.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B3IfElseStatement.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B3IfElseStatement.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B3IfElseStatement.py diff --git a/Learning_Materials/Beginner_Level/Python/B4CheckIn.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B4CheckIn.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B4CheckIn.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B4CheckIn.py diff --git a/Learning_Materials/Beginner_Level/Python/B5Functions.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B5Functions.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B5Functions.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B5Functions.py diff --git a/Learning_Materials/Beginner_Level/Python/B6Scope.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B6Scope.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B6Scope.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B6Scope.py diff --git a/Learning_Materials/Beginner_Level/Python/B7ClassesAndObjects.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B7ClassesAndObjects.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B7ClassesAndObjects.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B7ClassesAndObjects.py diff --git a/Learning_Materials/Beginner_Level/Python/B8FinalCheckIn.py b/2024-2025/Learning_Materials/Beginner_Level/Python/B8FinalCheckIn.py similarity index 100% rename from Learning_Materials/Beginner_Level/Python/B8FinalCheckIn.py rename to 2024-2025/Learning_Materials/Beginner_Level/Python/B8FinalCheckIn.py diff --git a/Learning_Materials/Intermediate_Level/Intermediate_Intro.txt b/2024-2025/Learning_Materials/Intermediate_Level/Intermediate_Intro.txt similarity index 100% rename from Learning_Materials/Intermediate_Level/Intermediate_Intro.txt rename to 2024-2025/Learning_Materials/Intermediate_Level/Intermediate_Intro.txt diff --git a/Learning_Materials/Intermediate_Level/Java/I0JavaCheatsheet.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I0JavaCheatsheet.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I0JavaCheatsheet.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I0JavaCheatsheet.java diff --git a/Learning_Materials/Intermediate_Level/Java/I1ConvertingDatatypes.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I1ConvertingDatatypes.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I1ConvertingDatatypes.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I1ConvertingDatatypes.java diff --git a/Learning_Materials/Intermediate_Level/Java/I2Arrays.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I2Arrays.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I2Arrays.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I2Arrays.java diff --git a/Learning_Materials/Intermediate_Level/Java/I3Loops.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I3Loops.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I3Loops.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I3Loops.java diff --git a/Learning_Materials/Intermediate_Level/Java/I4CheckIn.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I4CheckIn.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I4CheckIn.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I4CheckIn.java diff --git a/Learning_Materials/Intermediate_Level/Java/I5Enums.java b/2024-2025/Learning_Materials/Intermediate_Level/Java/I5Enums.java similarity index 100% rename from Learning_Materials/Intermediate_Level/Java/I5Enums.java rename to 2024-2025/Learning_Materials/Intermediate_Level/Java/I5Enums.java diff --git a/Learning_Materials/Intermediate_Level/Python/I1ConvertingDatatypes.py b/2024-2025/Learning_Materials/Intermediate_Level/Python/I1ConvertingDatatypes.py similarity index 100% rename from Learning_Materials/Intermediate_Level/Python/I1ConvertingDatatypes.py rename to 2024-2025/Learning_Materials/Intermediate_Level/Python/I1ConvertingDatatypes.py diff --git a/Learning_Materials/Intermediate_Level/Python/I2Lists.py b/2024-2025/Learning_Materials/Intermediate_Level/Python/I2Lists.py similarity index 100% rename from Learning_Materials/Intermediate_Level/Python/I2Lists.py rename to 2024-2025/Learning_Materials/Intermediate_Level/Python/I2Lists.py diff --git a/Learning_Materials/Robot_Level/Robot_Intro.txt b/2024-2025/Learning_Materials/Robot_Level/Robot_Intro.txt similarity index 100% rename from Learning_Materials/Robot_Level/Robot_Intro.txt rename to 2024-2025/Learning_Materials/Robot_Level/Robot_Intro.txt diff --git a/2024-2025/Tests/main-bot/.gitignore b/2024-2025/Tests/main-bot/.gitignore new file mode 100644 index 0000000..cdbfbfa --- /dev/null +++ b/2024-2025/Tests/main-bot/.gitignore @@ -0,0 +1,178 @@ +# 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 +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ diff --git a/2024-2025/Tests/main-bot/.wpilib/wpilib_preferences.json b/2024-2025/Tests/main-bot/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a5ebb07 --- /dev/null +++ b/2024-2025/Tests/main-bot/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2024", + "teamNumber": 5098 +} \ No newline at end of file diff --git a/2024-2025/Tests/main-bot/WPILib-License.md b/2024-2025/Tests/main-bot/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/Tests/main-bot/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/2024-2025/Tests/main-bot/build.gradle b/2024-2025/Tests/main-bot/build.gradle new file mode 100644 index 0000000..1e8da00 --- /dev/null +++ b/2024-2025/Tests/main-bot/build.gradle @@ -0,0 +1,101 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2024.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' + } + } + } + } +} + +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 = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + 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/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar b/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.properties b/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7015f6b --- /dev/null +++ b/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2024-2025/Tests/main-bot/gradlew b/2024-2025/Tests/main-bot/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/2024-2025/Tests/main-bot/gradlew @@ -0,0 +1,249 @@ +#!/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. +# + +############################################################################## +# +# 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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || 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/2024-2025/Tests/main-bot/gradlew.bat b/2024-2025/Tests/main-bot/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/2024-2025/Tests/main-bot/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@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. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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/2024-2025/Tests/main-bot/settings.gradle b/2024-2025/Tests/main-bot/settings.gradle new file mode 100644 index 0000000..3e30f84 --- /dev/null +++ b/2024-2025/Tests/main-bot/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2024' + 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/2024-2025/Tests/main-bot/src/main/deploy/example.txt b/2024-2025/Tests/main-bot/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/Tests/main-bot/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/2024-2025/Tests/main-bot/src/main/java/frc/robot/Main.java b/2024-2025/Tests/main-bot/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2024-2025/Tests/main-bot/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/2024-2025/Tests/main-bot/src/main/java/frc/robot/Robot.java b/2024-2025/Tests/main-bot/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..3254294 --- /dev/null +++ b/2024-2025/Tests/main-bot/src/main/java/frc/robot/Robot.java @@ -0,0 +1,72 @@ +// 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.TimesliceRobot; +import edu.wpi.first.wpilibj.livewindow.LiveWindow; + +/** + * The VM is configured to automatically run this class, and to call the + * functions corresponding to each mode, as described in the TimesliceRobot + * 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 TimesliceRobot { + + /** Robot constructor. */ + public Robot() { + // Run robot periodic() functions for 5 ms, and run controllers every 10 ms + super(0.005, 0.01); + + // LiveWindow causes drastic overruns in robot periodic functions that will + // interfere with controllers + LiveWindow.disableAllTelemetry(); + + // Runs for 2 ms after robot periodic functions + schedule(() -> {}, 0.002); + + // Runs for 2 ms after first controller function + schedule(() -> {}, 0.002); + + // Total usage: + // 5 ms (robot) + 2 ms (controller 1) + 2 ms (controller 2) = 9 ms + // 9 ms / 10 ms -> 90% allocated + } + + /** + * This function is run when the robot is first started up and should be used + * for any initialization code. + */ + @Override + public void robotInit() {} + + @Override + public void robotPeriodic() {} + + @Override + public void autonomousInit() {} + + @Override + public void autonomousPeriodic() {} + + @Override + public void teleopInit() {} + + @Override + public void teleopPeriodic() {} + + @Override + public void disabledInit() {} + + @Override + public void disabledPeriodic() {} + + @Override + public void testInit() {} + + @Override + public void testPeriodic() {} +} diff --git a/2024-2025/Tests/main-bot/vendordeps/WPILibNewCommands.json b/2024-2025/Tests/main-bot/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..67bf389 --- /dev/null +++ b/2024-2025/Tests/main-bot/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": "2024", + "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/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore b/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore new file mode 100644 index 0000000..cdbfbfa --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore @@ -0,0 +1,178 @@ +# 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 +*-window.json + +# Simulation data log directory +logs/ + +# Folder that has CTRE Phoenix Sim device config storage +ctre_sim/ diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/.wpilib/wpilib_preferences.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/.wpilib/wpilib_preferences.json new file mode 100644 index 0000000..a5ebb07 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/.wpilib/wpilib_preferences.json @@ -0,0 +1,6 @@ +{ + "enableCppIntellisense": false, + "currentLanguage": "java", + "projectYear": "2024", + "teamNumber": 5098 +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/WPILib-License.md b/2024-2025/Win-Nguyen/Win-Nguyen-2024/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/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/2024-2025/Win-Nguyen/Win-Nguyen-2024/build.gradle b/2024-2025/Win-Nguyen/Win-Nguyen-2024/build.gradle new file mode 100644 index 0000000..1e8da00 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/build.gradle @@ -0,0 +1,101 @@ +plugins { + id "java" + id "edu.wpi.first.GradleRIO" version "2024.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' + } + } + } + } +} + +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 = false + +// Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. +// Also defines JUnit 5. +dependencies { + 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/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.properties b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7015f6b --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=permwrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=permwrapper/dists diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew @@ -0,0 +1,249 @@ +#!/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. +# + +############################################################################## +# +# 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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || 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/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew.bat b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@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. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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/2024-2025/Win-Nguyen/Win-Nguyen-2024/settings.gradle b/2024-2025/Win-Nguyen/Win-Nguyen-2024/settings.gradle new file mode 100644 index 0000000..3e30f84 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/settings.gradle @@ -0,0 +1,30 @@ +import org.gradle.internal.os.OperatingSystem + +pluginManagement { + repositories { + mavenLocal() + gradlePluginPortal() + String frcYear = '2024' + 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/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/example.txt b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/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/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/navgrid.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/navgrid.json new file mode 100644 index 0000000..bab0da9 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/navgrid.json @@ -0,0 +1 @@ +{"field_size":{"x":16.54,"y":8.21},"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,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,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,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,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,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,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,true,true],[true,true,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,false,false,false,true,true,true,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,true,true,true,true,true,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,true,true],[true,true,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,true,true,true,true,true,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,true,true,true,true,true,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,true,true],[true,true,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,true,true],[true,true,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,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true],[true,true,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,true,true,true,true,true,false,false,false,false,false,false,false,true,true,true],[true,true,true,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,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,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,true,true,true,true,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,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,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,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,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,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,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,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,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,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,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,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,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,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,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/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/paths/newpath.path b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/paths/newpath.path new file mode 100644 index 0000000..f7fb046 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/pathplanner/paths/newpath.path @@ -0,0 +1,49 @@ +{ + "version": 1.0, + "waypoints": [ + { + "anchor": { + "x": 0.0, + "y": 0.0 + }, + "prevControl": null, + "nextControl": { + "x": 1.0, + "y": 0.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 1.0, + "y": 0.0 + }, + "prevControl": { + "x": 0.0, + "y": 0.0 + }, + "nextControl": null, + "isLocked": false, + "linkedName": null + } + ], + "rotationTargets": [], + "constraintZones": [], + "eventMarkers": [], + "globalConstraints": { + "maxVelocity": 3.0, + "maxAcceleration": 3.0, + "maxAngularVelocity": 540.0, + "maxAngularAcceleration": 720.0 + }, + "goalEndState": { + "velocity": 0, + "rotation": 0.0, + "rotateFast": false + }, + "reversed": false, + "folder": null, + "previewStartingState": null, + "useDefaultConstraints": false +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/robot_settings.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/robot_settings.json new file mode 100644 index 0000000..5e10b2f --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/deploy/robot_settings.json @@ -0,0 +1,33 @@ +{ + "controllerID": 0, + "swerveDrive": { + "frontLeft": { + "driveID": 1, + "turnID": 2, + "encoderID": 10, + "posY": -0.23, + "posX": 0.36 + }, + "frontRight": { + "driveID": 7, + "turnID": 8, + "encoderID": 12, + "posY": 0.23, + "posX": 0.36 + }, + "backLeft": { + "driveID": 3, + "turnID": 4, + "encoderID": 11, + "posY": -0.23, + "posX": -0.36 + }, + "backRight": { + "driveID": 5, + "turnID": 6, + "encoderID": 9, + "posY": 0.23, + "posX": -0.36 + } + } +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/CommonData.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/CommonData.java new file mode 100644 index 0000000..7d7148b --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/CommonData.java @@ -0,0 +1,214 @@ +package frc; + +// import frc.robot.Utility; + +/** +* Data used by other classes. +*

+* This is used to store data that needs to be accessed by multiple classes. +*

+*/ +public final class CommonData { + + // Swerve Drive Variables // + + /** + * desiredTurn is the desired angle of the robot. + */ + private static double desiredTurn; + + /** + * forwardSpeed is the desired forward and backward speed of the robot. + */ + private static double forwardSpeed; + + /** + * sideSpeed is the desired left and right speed of the robot. + */ + private static double sideSpeed; + + /** + * battenDownTheHatches is used to control the defensive stance. + */ + private static boolean battenDownTheHatches; + + // NavX Variables // + + /** + * calibrate is used to determine if the NavX should be calibrated. + */ + private static boolean calibrate; + + // Autonomous Varaibles // + + /** + * counter is used in the state machine to see what state the robot is in. + */ + private static int counter; + + private static boolean followPath; + + // private static boolean shuffleBoardPID; + + // /* + // * Would like to convert to ENUM at some point with (FL, FR, Bl, BR) - Henry + // * Used to determine which turn motor the PID settings will be tuning + // */ + // private static String tuningMotor; + + // private static boolean[] tuningMotorArray = {true, false, false, false}; + + // Constructor // + // This is set to private as this is a utility class and + // should not be instantiated + private CommonData() { + throw new UnsupportedOperationException( + "This is a utility class and cannot be instantiated"); + } + + // Accesor Methods // + + /** + * Method for returning desiredTurn. + *s + * @return desiredTurn + */ + public static double getDesiredTurn() { + return desiredTurn; + } + + /** + * Method for setting the desiredTurn. + * + * @param value value to set desiredTurn + */ + public static void setDesiredTurn(final double value) { + desiredTurn = value; + } + + /** + * Method for returning forwardSpeed. + * + * @return forwardSpeed + */ + public static double getForwardSpeed() { + return forwardSpeed; + } + + /** + * Method for setting forwardSpeed. + * + * @param value value to set forwardSpeed + */ + public static void setForwardSpeed(final double value) { + forwardSpeed = value; + } + + /** + * Method for returning sideSpeed. + * + * @return sideSpeed + */ + public static double getSideSpeed() { + return sideSpeed; + } + + /** + * Method for setting sideSpeed. + * + * @param value value to set sideSpeed + */ + public static void setSideSpeed(final double value) { + sideSpeed = value; + } + + public static void setFollowPath(final boolean value){ + followPath = value; + } + + public static boolean getFollowPath(){ + return followPath; + } + + /** + * Method for returning battenDownTheHatches. + * + * @return battenDownTheHatches + */ + public static boolean getBattenDownTheHatches() { + return battenDownTheHatches; + } + + /** + * Method for setting battenDownTheHatches. + * + * @param value value to set battenDownTheHatches + */ + public static void setBattenDownTheHatches(final boolean value) { + battenDownTheHatches = value; + } + + /** + * Method for returning calibrate. + * + * @return calibrate + */ + public static boolean getCalibrate() { + return calibrate; + } + + /** + * Method for setting calibrate. + * + * @param value value to set calibrate + */ + public static void setCalibrate(final boolean value) { + calibrate = value; + } + + /** + * Method for returning counter. + * + * @return counter + */ + public static int getCounter() { + return counter; + } + + /** + * Method for setting counter. + * + * @param value value to set counter + */ + public static void setCounter(final int value) { + counter = value; + } + + /** + * Method for displaying the PID values + * + */ + // public static boolean getShuffleBoardPID() { + // return shuffleBoardPID; + // } + /** + * Motor you are tuning + */ + // public static String setTuningMotor(String newTuningMotor) { //used to set turning motor in common data + // // If statement to make sure the chosen tuning motor is one of the four available + // if (newTuningMotor.equals("FL") || newTuningMotor.equals("FR") || newTuningMotor.equals("BL") || newTuningMotor.equals("BR")){ + // tuningMotor = newTuningMotor; + // return tuningMotor; + // } else { + // // need to add some debug statement + // Utility.printLn("That's not a tuning motor :("); + // return tuningMotor; + // } + // } + // public static String getTuningMotor() { + // return tuningMotor; + // } + // public static Boolean getTuningMotorArray() { + + // } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/IComponent.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/IComponent.java new file mode 100644 index 0000000..2f57cf7 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/IComponent.java @@ -0,0 +1,18 @@ +package frc.components; + +/** + * Interface for components. + */ +public interface IComponent { + + // This is an interface. + // If a class implements any interface, + // it must have all of the methods in that interface. + // The methods defined in an interface are abstract, + // meaning they have no body. + + /** + * Method for updating the component. + */ + void update(); +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveDrive.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveDrive.java new file mode 100644 index 0000000..145dcb7 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveDrive.java @@ -0,0 +1,238 @@ +package frc.components; + +import com.kauailabs.navx.frc.AHRS; +import com.pathplanner.lib.controllers.PPHolonomicDriveController; +import com.pathplanner.lib.path.PathPlannerPath; +import com.pathplanner.lib.path.PathPlannerTrajectory; +import com.pathplanner.lib.util.PIDConstants; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.wpilibj.SPI; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import frc.CommonData; +import frc.robot.Settings; +import frc.robot.Utility; + +// NOTE: All things commented out are not used. (At least not currently) + +/** + * + */ +public class SwerveDrive implements IComponent { + + /** + * Overall Equation: + * + * Motor roations per second: + * Motor rotations per minute / 60 + * + * Wheel rotations per second: + * Motor rotations per second / gear ratio + * + * Wheel radius (in ft): + * Wheel diameter (in ft) / 2 + * or + * Wheel radius (in inches) / 12 + * + * Wheel circumference (in ft): + * Wheel radius (in ft) * 2 * pi + * + * Speed (in ft/s): + * Wheel rotations per second * wheel circumference (in ft) + * + * Total Equation: + * (Motor rotations per minute / 60) / gear ratio * wheel circumference + * (in ft) + */ + + /** + * The maximum rotation of the robot. + */ + private final static double MAX_ROTATION = + (SwerveWheel.MAX_SPEED * 2 * Math.PI) / 2.7389195456 / 2; + + // The 2.7389195456 value still needs to be documented + + /** + * The offset of the yaw. + */ + double yawOffset = 0; + + /** + * The constructor for the SwerveDrive class. + * @param settings The settings for the swerve drive. + */ + public SwerveDrive(final Settings.SwerveDrive settings) { + swerveWheels = new SwerveWheel[] {new SwerveWheel(settings.frontLeft), + new SwerveWheel(settings.frontRight), + new SwerveWheel(settings.backLeft), + new SwerveWheel(settings.backRight)}; + + kinematics = new SwerveDriveKinematics( + swerveWheels[0].getSwervePos(), swerveWheels[1].getSwervePos(), + swerveWheels[2].getSwervePos(), swerveWheels[3].getSwervePos()); + + var path = PathPlannerPath.fromPathFile("newpath"); + traj = path.getTrajectory(new ChassisSpeeds(0, 0, 0), new Rotation2d(0)); + + odometry = new SwerveDrivePoseEstimator( + kinematics, Rotation2d.fromDegrees(360.0 - ahrs.getYaw()), + new SwerveModulePosition[] { + swerveWheels[0].getOdometryPosition(), + swerveWheels[1].getOdometryPosition(), + swerveWheels[2].getOdometryPosition(), + swerveWheels[3].getOdometryPosition(), + }, + new Pose2d(0, 0, new Rotation2d(0))); + + // limelightPoses[0] = new LimelightPose("limelight-back"); + // limelightPoses[1] = new LimelightPose("limelight-right"); + // limelightPoses[2] = new LimelightPose("limelight-left"); + + controller = new PPHolonomicDriveController( + new PIDConstants(0.5, 0, 0), new PIDConstants(0.05, 0, 0), + SwerveWheel.MAX_SPEED, 0.42720018726587655839358241631199); + } + + SwerveWheel[] swerveWheels; + + double leftOrRight; + double forwardOrBack; // This is the forward or back speed + + PathPlannerTrajectory traj; + PPHolonomicDriveController controller; + + private AHRS ahrs = new AHRS(SPI.Port.kMXP); + + Timer timer; + + private SwerveDriveKinematics kinematics; + private SwerveDrivePoseEstimator odometry; + + // LimelightPose[] limelightPoses = new LimelightPose[3]; + + Pose2d getCurrentPose() { return odometry.getEstimatedPosition(); } + + public void update() { + + // Put the wheels in their "X" position + // TODO: Add the common data value and finish implementing the code + // if (CommonData.getDefense) { + // } + + // Calabrate The NavX + if (CommonData.getCalibrate()) { + CommonData.setCalibrate(false); + // Reset the NavX. + ahrs.reset(); + // Reset the yaw offset after calibration. + yawOffset = ahrs.getYaw(); + } else { + odometry.update(Rotation2d.fromDegrees(360.0 - ahrs.getYaw()), + new SwerveModulePosition[] { + swerveWheels[0].getOdometryPosition(), + swerveWheels[1].getOdometryPosition(), + swerveWheels[2].getOdometryPosition(), + swerveWheels[3].getOdometryPosition(), + }); + } + + SmartDashboard.putNumber("Yaw offset", yawOffset); + + // Optional maybeClosest = + // Stream.of(limelightPoses) + // .filter(x -> x.validPose()) + // .min((left, right) -> { + // final var whereWeAre = + // odometry.getEstimatedPosition().getTranslation(); + // final var leftDistance = + // left.getPose().getTranslation().getDistance(whereWeAre); + // final var rightDistance = + // right.getPose().getTranslation().getDistance(whereWeAre); + // return leftDistance < rightDistance ? -1 + // : leftDistance > rightDistance ? 1 + // : 0; + // }); + + // if (maybeClosest.isPresent()) { + // odometry.addVisionMeasurement(maybeClosest.get().getPose(), + // Timer.getFPGATimestamp()); + // } + + if (CommonData.getFollowPath()) { + if (timer == null) { + timer = new Timer(); + timer.start(); + } + + if (timer.get() >= traj.getTotalTimeSeconds()) { + // CommonData.setFollowPath(false); + // return; + } + + var pose = getCurrentPose(); + var goal = traj.sample(timer.get()); + + StringBuilder diagnosticString = new StringBuilder(); + + diagnosticString.append("\n\nCurrent pose"); + diagnosticString.append("\nX: " + pose.getX()); + diagnosticString.append("\nY: " + pose.getY()); + diagnosticString.append("\nCurrent goal"); + diagnosticString.append("\nX: " + goal.positionMeters.getX()); + diagnosticString.append("\nY: " + goal.positionMeters.getY()); + + var speeds = + controller.calculateRobotRelativeSpeeds(getCurrentPose(), goal); + + diagnosticString.append("\nCalculated Speeds"); + diagnosticString.append("\nX/s: " + speeds.vxMetersPerSecond); + diagnosticString.append("\nY/s: " + speeds.vyMetersPerSecond); + diagnosticString.append("\nR/s: " + + (speeds.omegaRadiansPerSecond / (2 * Math.PI))); + + var states = kinematics.toSwerveModuleStates(speeds); + + diagnosticString.append("\nSwerve Wheel States"); + + for (int i = 0; i < 4; ++i) { + diagnosticString.append("\n[" + i + "] M/s" + + states[i].speedMetersPerSecond); + diagnosticString.append("\n[" + i + "] Ang" + + states[i].angle.getRotations()); + + swerveWheels[i].set(states[i]); + } + + Utility.printLn(diagnosticString.toString()); + } else { + timer = null; + + final var desiredTranslationSpeedX = + CommonData.getForwardSpeed() * + SwerveWheel.MAX_SPEED; // This code uses '+x' as toward opposing + // alliance wall + final var desiredTranslationSpeedY = + -CommonData.getSideSpeed() * + SwerveWheel.MAX_SPEED; // This code uses '+y' as toward driver's right + final var desiredRotationSpeed = + -CommonData.getDesiredTurn() * MAX_ROTATION; + var moduleStates = + kinematics.toSwerveModuleStates(ChassisSpeeds.fromFieldRelativeSpeeds( + desiredTranslationSpeedX, desiredTranslationSpeedY, + desiredRotationSpeed, Rotation2d.fromDegrees(-ahrs.getYaw()))); + + for (int i = 0; i < 4; i++) { + swerveWheels[i].set(moduleStates[i]); + } + } + } // Man that's a lot of curly braces - Justin +} // Well here's some more for ya {{}}{{{}{{{{}}}}}} - Henry +// Do you need some more? Here you go! {{{{{{{{{{{{}}}}}}}}}}}} - Ben +// :o +// >:p diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveWheel.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveWheel.java new file mode 100644 index 0000000..ac195b4 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/SwerveWheel.java @@ -0,0 +1,124 @@ +package frc.components; + +import com.ctre.phoenix6.controls.DutyCycleOut; +import com.ctre.phoenix6.controls.PositionDutyCycle; +import com.ctre.phoenix6.hardware.CANcoder; +import com.ctre.phoenix6.hardware.TalonFX; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import frc.robot.Settings; + +public class SwerveWheel { + /** + * Wheel circumference in meters. + */ + public final static double WHEEL_CIRCUMFERENCE = 0.3191858136; + + /** + * Drive gear ratio of the swerve modules from motor rotations to wheel + * rotations + */ + public final static double DRIVE_GEAR_RATIO = 6.55; + + /** + * The Falcon 500's maximum rated RPM. + */ + public final static int MAXIMUM_FALCON_RPM = 6380; + + /** + * The wheel's maximum rated RPM using a Falcon 500. + */ + public final static double MAXIMUM_WHEEL_RPM = + MAXIMUM_FALCON_RPM / DRIVE_GEAR_RATIO; + + /** + * The Kraken's RPM + */ + public final static int KRAKEN_RPM = 6000; + + /** + * The maximum speed of the robot in meters per second. + * + *

+ * 60 is used to convert from minutes to seconds. + *

+ */ + public final static double MAX_SPEED = + MAXIMUM_WHEEL_RPM * WHEEL_CIRCUMFERENCE / 60; + + TalonFX turnMotor; + TalonFX driveMotor; + CANcoder encoder; + + // 0.238125 Meters + // 0.365125 Meters + + Translation2d modulePos; + + double defensiveAngle; + + public SwerveWheel(Settings.SwerveDrive.Wheel settings) { + turnMotor = new TalonFX(settings.turnID); + driveMotor = new TalonFX(settings.driveID); + encoder = new CANcoder(settings.encoderID); + + modulePos = new Translation2d(settings.posX, settings.posY); + } + + public void set(SwerveModuleState swerveModuleState) { + // swerveModuleState = SwerveModuleState.optimize(swerveModuleState, + // Rotation2d.fromDegrees(turnMotor.getSelectedSensorPosition() + // / 11.3777778)); + swerveModuleState = SwerveModuleState.optimize( + swerveModuleState, + Rotation2d.fromDegrees( + encoder.getAbsolutePosition().getValueAsDouble() * 360)); + + // final double desiredTurnPos = swerveModuleState.angle.getDegrees() + // * 11.3777778; // conversion from degrees to native encoder value + final double desiredTurnPos = + swerveModuleState.angle.getDegrees() / + 360; // conversion from degrees to native encoder value + + // turnMotor.set(ControlMode.Position, desiredTurnPos); + turnMotor.setControl(new PositionDutyCycle(desiredTurnPos)); + + // driveMotor.set(ControlMode.PercentOutput, + // swerveModuleState.speedMetersPerSecond / maximumSpeed); + driveMotor.setControl( + new DutyCycleOut(swerveModuleState.speedMetersPerSecond / MAX_SPEED)); + // Using Velocity Can Be Used For Better Control But Percent Output Is + // Better Used For Practice + } + + public void defense() { + // turnMotor.set(ControlMode.Position, defensiveAngle * 11.77777777); + // driveMotor.set(ControlMode.PercentOutput, 0); + } + + public SwerveModulePosition getOdometryPosition() { + return new SwerveModulePosition( + driveMotor.getPosition().getValueAsDouble() / DRIVE_GEAR_RATIO * + WHEEL_CIRCUMFERENCE, + Rotation2d.fromRotations( + encoder.getAbsolutePosition().getValueAsDouble())); + } + + public TalonFX getTurnMotor() { return turnMotor; } + + public TalonFX getDriveMotor() { return driveMotor; } + + public Translation2d getSwervePos() { return modulePos; } + // public TalonFXConfigurator findConfig() { + // TalonFXConfigurator testValue = turnMotor.getConfigurator(); + // testValue. + // return turnMotor.getConfigurator(); + // } + + // public void battenDownTheHatches() + // { + // set(defensiveAngle, 0, 0); + // } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/autonomous/Autonomous.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/autonomous/Autonomous.java new file mode 100644 index 0000000..814b3c7 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/autonomous/Autonomous.java @@ -0,0 +1,58 @@ +package frc.components.autonomous; + +import java.io.IOException; + +import edu.wpi.first.math.controller.HolonomicDriveController; +import edu.wpi.first.math.controller.PIDController; +import edu.wpi.first.math.controller.ProfiledPIDController; +import edu.wpi.first.math.trajectory.Trajectory; +import edu.wpi.first.math.trajectory.TrajectoryUtil; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Filesystem; +import frc.components.IComponent; +import frc.state_machine.StateMachine; + +/** + * Autonomous component. + *

+ * This class is used for autonomous mode. + * It sets up a state machine and excutes the states contained. + *

+ *

+ * If you want to add a new state, see the constructor. + * {@link frc.components.autonomous.Autonomous#Autonomous()} + *

+ * This class implements the + * {@link frc.components.IComponent IComponent} interface. + *

+ * + * @see frc.state_machine.StateMachine + */ +public final class Autonomous implements IComponent { + + /** + * Autonomous constructor. + *

+ * This is where new states are added to the state machine. + * Any state added here will be run during autonomous in + * the order they are added. + *

+ *

+ * You can add states by using the following code: + * + *

+     * stateMachine.addState(new StateName());
+     * 
+ * + * Any added states must also exist in this directory as separate + * classes. + *

+ * + * @see frc.components.autonomous.DriveState1 + */ + public Autonomous() {} + + @Override + public void update() { + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/package-info.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/package-info.java new file mode 100644 index 0000000..74893cd --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/components/package-info.java @@ -0,0 +1,7 @@ +/** + * Package used to organize all of the components of the robot. + *

+ * These components can include the drivetrain, an arm, an intake, etc. + *

+ */ +package frc.components; diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/LogitechF310.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/LogitechF310.java new file mode 100644 index 0000000..74a8997 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/LogitechF310.java @@ -0,0 +1,463 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.Joystick; + +/** + * This class is used to create an LogitechF310 controller object. + *

+ * This class uses the {@code Joystick} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see PS4 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.Joystick + */ +public class LogitechF310 { + + // Button IDs // + + /** + * The axis ID for the left joystick x. + */ + private static final int JOYSTICK_LEFT_X = 0; + + /** + * The axis ID for the left joystick y. + */ + private static final int JOYSTICK_LEFT_Y = 1; + + /** + * The axis ID for the left trigger. + */ + private static final int TRIGGER_LEFT = 2; + + /** + * The axis ID for the right trigger. + */ + private static final int TRIGGER_RIGHT = 3; + + /** + * The axis ID for the right joystick x. + */ + private static final int JOYSTICK_RIGHT_X = 4; + + /** + * The axis ID for the right joystick y. + */ + private static final int JOYSTICK_RIGHT_Y = 5; + + /** + * The button ID for the A button. + */ + private static final int BUTTON_A = 1; + + /** + * The button ID for the B button. + */ + private static final int BUTTON_B = 2; + + /** + * The button ID for the X button. + */ + private static final int BUTTON_X = 3; + + /** + * The button ID for the Y button. + */ + private static final int BUTTON_Y = 4; + + /** + * The button ID for the left bumper. + */ + private static final int BUTTON_LEFT_BUMPER = 5; + + /** + * The button ID for the right bumper. + */ + private static final int BUTTON_RIGHT_BUMPER = 6; + + /** + * The button ID for the start button. + */ + private static final int BUTTON_START = 7; + + /** + * The button ID for the back button. + */ + private static final int BUTTON_BACK = 8; + + /** + * The controller object. + * In this case, a {@code Joystick} object. + * + * @see edu.wpi.first.wpilibj.Joystick + */ + private Joystick controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the LogitechF310 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public LogitechF310(final int id, final double deadBandValue) { + controller = new Joystick(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_X)); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_Y)); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_X)); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_Y)); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return Value of the left trigger + * + * @see LogitechF310#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_LEFT)); + } + + /** + * Method for returning the value of the right trigger. + * + * @return Value of the right trigger + * + * @see LogitechF310#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_RIGHT)); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return Value of the A button + * + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getAButton() { + return controller.getRawButton(BUTTON_A); + } + + /** + * Method for returning the value of the B button. + * + * @return Value of the B button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getBButton() { + return controller.getRawButton(BUTTON_B); + } + + /** + * Method for returning the value of the X button. + * + * @return Value of the X button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getYButton + */ + public boolean getXButton() { + return controller.getRawButton(BUTTON_X); + } + + /** + * Method for returning the value of the Y button. + * + * @return Value of the Y button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + */ + public boolean getYButton() { + return controller.getRawButton(BUTTON_Y); + } + + /** + * Method for returning the value of the back button. + * + * @return Value of the back button + * + * @see LogitechF310#getStartButton + */ + public boolean getBackButton() { + return controller.getRawButton(BUTTON_START); + } + + /** + * Method for returning the value of the start button. + * + * @return Value of the start button + * + * @see LogitechF310#getBackButton + */ + public boolean getStartButton() { + return controller.getRawButton(BUTTON_BACK); + } + + /** + * Method for returning the value of the left bumper. + * + * @return Value of the left bumper + * + * @see LogitechF310#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getRawButton(BUTTON_LEFT_BUMPER); + } + + /** + * Method for returning the value of the right bumper. + * + * @return Value of the right bumper + * + * @see LogitechF310#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRawButton(BUTTON_RIGHT_BUMPER); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see LogitechF310.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + // This controller does not have rumble functionality :( + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/PS4.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/PS4.java new file mode 100644 index 0000000..cef2652 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/PS4.java @@ -0,0 +1,498 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.PS4Controller; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an PS4 controller object. + *

+ * This class uses the {@code PS4Controller} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ +public class PS4 { + + /** + * The controller object. + * In this case, a {@code PS4Controller} object. + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ + private PS4Controller controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the PS4 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public PS4(final int id, final double deadBandValue) { + controller = new PS4Controller(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see PS4#getLeftX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see PS4#getRightX + * @see PS4#getLeftX + * @see PS4#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see PS4#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getL2Axis()); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see PS4#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getR2Axis()); + } + + // Button Methods // + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getXButton() { + return controller.getCrossButton(); + } + + /** + * Method for returning the value of the Square button. + * + * @return value of the Square button + * + * @see PS4#getXButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getSquareButton() { + return controller.getSquareButton(); + } + + /** + * Method for returning the value of the Circle button. + * + * @return value of the Circle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getCircleButton() { + return controller.getCircleButton(); + } + + /** + * Method for returning the value of the Triangle button. + * + * @return value of the Triangle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getTriangleButton() { + return controller.getTriangleButton(); + } + + /** + * Method for returning the value of the Share button. + * + * @return value of the Share button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getShareButton() { + return controller.getShareButton(); + } + + /** + * Method for returning the value of the Options button. + * + * @return value of the Options button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getOptionsButton() { + return controller.getOptionsButton(); + } + + /** + * Method for returning the value of the PlayStation button. + * + * @return value of the PlayStation button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getTouchpadButton + */ + public boolean getPlayStationButton() { + return controller.getPSButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see PS4#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getL1Button(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see PS4#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getR1Button(); + } + + /** + * Method for returning the value of the left joystick button. + * + * @return value of the left joystick button + * + * @see PS4#getRightJoystickButton + */ + public boolean getLeftJoystickButton() { + return controller.getL3Button(); + } + + /** + * Method for returning the value of the right joystick button. + * + * @return value of the right joystick button + * + * @see PS4#getLeftJoystickButton + */ + public boolean getRightJoystickButton() { + return controller.getR3Button(); + } + + // Touchpad Methods // + + /** + * Method for returning the value of the Touchpad button. + * + * @return value of the Touchpad button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + */ + public boolean getTouchpadButton() { + return controller.getTouchpad(); + } + + // There is a 'touchpad' that uses an event loop + // but for the purpose of this class, it is not needed + // It might be added in a future version if there becomes a need for it + // If you have a need for it or have added it yourself, + // Submit a pull request or submit an issue on the GitHub repository + // https://github.com/J-The-Fox/FRC-Team-5098 + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see PS4.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/Xbox.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/Xbox.java new file mode 100644 index 0000000..c8cc131 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/Xbox.java @@ -0,0 +1,425 @@ +package frc.controllers; + +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an Xbox controller object. + *

+ * This class uses the {@code XboxController} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see PS4 + * + * @see edu.wpi.first.wpilibj.XboxController + */ +public class Xbox { + + /** + * The controller object. + * In this case, a {@code XboxController} object. + */ + private XboxController controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the Xbox class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public Xbox(final int id, final double deadBandValue) { + controller = new XboxController(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see Xbox#getRightY + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see Xbox#getRightX + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see Xbox#getRightTrigger + */ + public double getLeftTrigger() { + return controller.getLeftTriggerAxis(); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see Xbox#getLeftTrigger + */ + public double getRightTrigger() { + return controller.getRightTriggerAxis(); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return value of the A button + * + * @see Xbox#getBButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getAButton() { + return controller.getAButton(); + } + + /** + * Method for returning the value of the B button. + * + * @return value of the B button + * + * @see Xbox#getAButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getBButton() { + return controller.getBButton(); + } + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getYButton + */ + public boolean getXButton() { + return controller.getXButton(); + } + + /** + * Method for returning the value of the Y button. + * + * @return value of the Y button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getXButton + */ + public boolean getYButton() { + return controller.getYButton(); + } + + /** + * Method for returning the value of the start button. + * + * @return value of the start button + * + * @see Xbox#getBackButton + */ + public boolean getStartButton() { + return controller.getStartButton(); + } + + /** + * Method for returning the value of the back button. + * + * @return value of the back button + * + * @see Xbox#getStartButton + */ + public boolean getBackButton() { + return controller.getBackButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see Xbox#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getLeftBumper(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see Xbox#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRightBumper(); + } + + /** + * Method for returning the value of the left stick button. + * + * @return value of the left stick button + * + * @see Xbox#getRightStickButton + */ + public boolean getLeftStickButton() { + return controller.getLeftStickButton(); + } + + /** + * Method for returning the value of the right stick button. + * + * @return value of the right stick button + * + * @see Xbox#getLeftStickButton + */ + public boolean getRightStickButton() { + return controller.getRightStickButton(); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see Xbox.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/package-info.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/package-info.java new file mode 100644 index 0000000..80793a8 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/controllers/package-info.java @@ -0,0 +1,7 @@ +/** + * Holds controller classes + *

+ * This can be used to hold both custom and pre-made controller classes. + *

+ */ +package frc.controllers; diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/package-info.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/package-info.java new file mode 100644 index 0000000..36e2dec --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/package-info.java @@ -0,0 +1,7 @@ +/** + * Main package for robot code. + *

+ * All code that is deployed to the robot would be located under this package. + *

+ */ +package frc; diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Main.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..48b72ed --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Main.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; + +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. + *

+ * + * @param args arguments from command line + */ + public static void main(final String... args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Robot.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..cd92b7c --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Robot.java @@ -0,0 +1,179 @@ +// 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; + +// Imports // +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.wpi.first.wpilibj.Filesystem; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; +import edu.wpi.first.wpilibj.TimedRobot; +import frc.CommonData; +import frc.components.IComponent; +import frc.components.SwerveDrive; +import frc.components.autonomous.Autonomous; +import frc.controllers.PS4; +import frc.controllers.Xbox; +import java.io.IOException; + +/** + * 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 final class Robot extends TimedRobot { + + /** + * The settings object. + * This is used to store the settings from the robot_settings.json file. + */ + private static Settings settings; + static { + ObjectMapper mapper = new ObjectMapper(); + try { + var filepath = Filesystem.getDeployDirectory().toPath().resolve( + "robot_settings.json"); + settings = mapper.readValue(filepath.toFile(), Settings.class); + } catch (IOException ex) { + System.out.println(ex.toString()); + } + } + + // Initialize the controllers // + // NOTE: This might be used for a more dynamic way of selecting controllers + + /** + * The main controller. + * This is used for the main driver. + *

+ * This is an {@code Xbox} object. + *

+ *

+ * Notice: This might be changed in the future. + *

+ */ + private Xbox controller = new Xbox(settings.controllerID, 0.1); + + /** + * The auxiliary controller. + * This is used for the auxiliary driver. + *

+ * This is a {@code PS4} object. + *

+ *

+ * Notice: This might be changed in the future. + *

+ */ + // private PS4 auxController = new PS4(settings.auxControllerID, 0); + + // Set up the components + /** + * The components array. + * This is used to store all of the components. + *

+ * This is an array of {@code IComponent} objects. + *

+ *

+ * Note: More components will be added here. + *

+ */ + private IComponent[] components = + new IComponent[] {new SwerveDrive(settings.swerveDrive)}; + + /** + * The autonomous object. + */ + private Autonomous autonomous; + + @Override + public void robotInit() { + // Calibrate the gyro before the robot runs to ensure that it is accurate + CommonData.setCalibrate(true); + CommonData.setFollowPath(false); + } + + @Override + public void robotPeriodic() { + + // For each component in the components array, update it + // This calls the update method in each of the component classes + // Since this in the robotPeriodic method, + // this will apply to all modes (autonomous, teleop, etc.) + for (var actuator : components) { + actuator.update(); + } + } + + @Override + public void autonomousInit() { + // autonomous = new Autonomous(); + + CommonData.setFollowPath(true); + } + + @Override + public void autonomousPeriodic() { + // Update the autonomous states + // autonomous.update(); + } + + @Override + public void teleopInit() { + CommonData.setFollowPath(false); + } + + @Override + public void teleopPeriodic() { + + // Update the controller values // + + // Update values for the Swerve Drive + CommonData.setCalibrate(controller.getXButton()); + CommonData.setSideSpeed(controller.getLeftX()); + CommonData.setForwardSpeed(controller.getLeftY()); + CommonData.setDesiredTurn( + Utility.snapToEdge(-controller.getRightX(), -1, 1, 0.9)); + // Utility.UIUpdate(); + } + + @Override + public void disabledInit() { + CommonData.setFollowPath(false); + + // "Turn off" all functions of the robot // + + CommonData.setDesiredTurn(0); // Set the turn speed to 0 + CommonData.setForwardSpeed(0); // Set the drive speed to 0 + + // Turn off the rumble on any controllers + controller.setRumble(RumbleType.kBothRumble, 0); + // auxController.setRumble(RumbleType.kBothRumble, 0); + + CommonData.setCounter(0); // Set the counter to 0 (used in autonomous) + } + + @Override + public void disabledPeriodic() {} + + @Override + public void testInit() { + CommonData.setFollowPath(true); + } + + @Override + public void testPeriodic() { + // Utility.printLn("Test Periodic running"); + // Utility.UIUpdate(); + } + + @Override + public void simulationInit() {} + + @Override + public void simulationPeriodic() {} +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Settings.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Settings.java new file mode 100644 index 0000000..a5c7fc3 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Settings.java @@ -0,0 +1,26 @@ +package frc.robot; + +public class Settings { + + public class SwerveDrive { + + public class Wheel { + + public int driveID; + public int turnID; + public int encoderID; + + public double posX; + public double posY; + } + + public Wheel frontLeft; + public Wheel frontRight; + public Wheel backLeft; + public Wheel backRight; + } + + public SwerveDrive swerveDrive; + + public int controllerID; +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Utility.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Utility.java new file mode 100644 index 0000000..6464012 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/Utility.java @@ -0,0 +1,106 @@ +package frc.robot; + +// import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +// import frc.CommonData; +/** + * The {@code Utility} class is used to store general methods that are used + * throughout the code. + *

+ * Current methods include: + *

    + *
  • {@link Utility#printLn}
  • + *
  • {@link Utility#clamp}
  • + *
  • {@link Utility#snapToEdge}
  • + *
+ * Additional methods may be added in the future if and when they are needed. + *

+ */ +public final class Utility { + + // Constructor // + // This is set to private as this is a utility class and + // should not be instantiated + private Utility() { + throw new UnsupportedOperationException( + "This is a utility class and cannot be instantiated"); + } + + /** + * The {@code printCounter} variable is used to count how many times the + * {@code System.out.println} method has been called. + * This is used in the {@link Utility#printLn} method. + */ + private static int printCounter = 10; + + /** + * The {@code printLn} method is used to print a message to the + * console every 50 times it is called. + * This is to slow down the messages as it overwhlems the roboRIO + * and causes the console to lag. + * + * @param messageString String to print to the console + * + * @see Utility#printCounter + */ + public static void printLn(final String messageString) { + if (printCounter == 10) { + System.out.println(messageString); + printCounter = 0; + } else { + printCounter++; + } + } + + /** + * The {@code clamp} method is used to clamp a value between a minimum and + * maximum value. + *

+ * If the value is less than the minimum, the minimum is returned. + * If the value is greater than the maximum, the maximum is returned. + * If the value is between the minimum and maximum, the value is returned. + *

+ * + * @param value Value to clamp + * @param min Minimum value + * @param max Maximum value + * + * @return The clamped value + */ + public static double clamp(final double value, final double min, + final double max) { + return value < min ? min : value > max ? max : value; + } + + /** + * Snap a value to the minimum or maximum value only if it is within a + * threshold. + * Although similar to the {@link Utility#clamp} method, + * this method is used to snap a value to the minimum or maximum value + * if it iswithin a threshold + * + * @param value Value to snap + * @param min Minimum value + * @param max Maximum value + * @param threshold Threshold to snap to + * + * @return The snapped value + */ + public static double snapToEdge(final double value, final double min, + final double max, final double threshold) { + return value < -threshold ? min : value > threshold ? max : value; + } + // public static void UIUpdate() { + // // question for later, what is the key parameter necessary for methods + // // CommonData.setTuningMotor(SmartDashboard.getString("Change Tuning + // Motor", "FL")); SmartDashboard.putString("Tuning Motor", + // CommonData.getTuningMotor()); SmartDashboard.putBoolean("FL Button", + // true); SmartDashboard.putBoolean("FR Button", false); + // SmartDashboard.putBoolean("BL Button", false); + // SmartDashboard.putBoolean("BR Button", false); + // SmartDashboard.putBooleanArray("Motor Array", + // CommonData.tuningMotorArray); + // // SmartDashboard.getNumber("P Value", "") + // // SmartDashboard. + // Utility.printLn("UI update is running"); + // } +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/package-info.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/package-info.java new file mode 100644 index 0000000..20c110b --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/robot/package-info.java @@ -0,0 +1,7 @@ +/** + * Holds primary robot methods + *

+ * This package is automatically created when creating a new robot project. + *

+ */ +package frc.robot; diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/State.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/State.java new file mode 100644 index 0000000..d2450f0 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/State.java @@ -0,0 +1,37 @@ +package frc.state_machine; + +/** +* Abstract class used to create states for the state machine. +*/ +public abstract class State { + + // Imported from 2022-2023 code + + /** + * Method called when a state is entered. + */ + public void onEnter() { + } + + /** + * Method called when a state is exited. + */ + public void onExit() { + } + + /** + * Method called to check if a state is valid. + * @return boolean + */ + public boolean isValid() { + return true; + } + + /** + * Method called to run a state. + * This method should return true when the state is finished + * + * @return boolean + */ + public abstract boolean run(); +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/StateMachine.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/StateMachine.java new file mode 100644 index 0000000..3d9e73f --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/StateMachine.java @@ -0,0 +1,52 @@ +package frc.state_machine; + +import java.util.ArrayList; +import java.util.List; + +/** + * State Machine class for controlling a state machine. + * This is mainly used during auto. + * + * @see frc.components.autonomous.Autonomous + */ +public final class StateMachine { + + /** + * The current state of the state machine. + */ + private State currentState; + + /** + * The list of states to run. + */ + private List states = new ArrayList(); + + /** + * Adds a state to the state machine. + * + * @param state The state to add to the state machine. + */ + public void addState(final State state) { + states.add(state); + } + + /** + * Method called to run the state machine. + */ + public void run() { + if (!states.isEmpty()) { + if (currentState == null && states.get(0).isValid()) { + currentState = states.get(0); + currentState.onEnter(); + states.remove(0); + } + } + + if (currentState != null) { + if (currentState.run()) { + currentState.onExit(); + currentState = null; + } + } + } +} diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/package-info.java b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/package-info.java new file mode 100644 index 0000000..7486149 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/src/main/java/frc/state_machine/package-info.java @@ -0,0 +1,7 @@ +/** + * State machine package. + *

+ * This package contains all of the code for the state machine. + *

+ */ +package frc.state_machine; diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/NavX.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/NavX.json new file mode 100644 index 0000000..e978a5f --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/NavX.json @@ -0,0 +1,40 @@ +{ + "fileName": "NavX.json", + "name": "NavX", + "version": "2024.1.0", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "frcYear": "2024", + "mavenUrls": [ + "https://dev.studica.com/maven/release/2024/" + ], + "jsonUrl": "https://dev.studica.com/releases/2024/NavX.json", + "javaDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-frc-java", + "version": "2024.1.0" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-frc-cpp", + "version": "2024.1.0", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": false, + "libName": "navx_frc", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxraspbian", + "linuxarm32", + "linuxarm64", + "linuxx86-64", + "osxuniversal", + "windowsx86-64" + ] + } + ] +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/PathplannerLib.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/PathplannerLib.json new file mode 100644 index 0000000..6dc648d --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/PathplannerLib.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib.json", + "name": "PathplannerLib", + "version": "2024.2.8", + "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", + "frcYear": "2024", + "mavenUrls": [ + "https://3015rangerrobotics.github.io/pathplannerlib/repo" + ], + "jsonUrl": "https://3015rangerrobotics.github.io/pathplannerlib/PathplannerLib.json", + "javaDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-java", + "version": "2024.2.8" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2024.2.8", + "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/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/Phoenix6.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/Phoenix6.json new file mode 100644 index 0000000..2b7d172 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/Phoenix6.json @@ -0,0 +1,339 @@ +{ + "fileName": "Phoenix6.json", + "name": "CTRE-Phoenix (v6)", + "version": "24.2.0", + "frcYear": 2024, + "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-frc2024-latest.json", + "conflictsWith": [ + { + "uuid": "3fcf3402-e646-4fa6-971e-18afe8173b1a", + "errorMessage": "The combined Phoenix-6-And-5 vendordep is no longer supported. Please remove the vendordep and instead add both the latest Phoenix 6 vendordep and Phoenix 5 vendordep.", + "offlineFileName": "Phoenix6And5.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "24.2.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "24.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "24.2.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "24.2.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "24.2.0", + "libName": "CTRE_Phoenix6_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "24.2.0", + "libName": "CTRE_PhoenixTools_Sim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "24.2.0", + "libName": "CTRE_SimTalonSRX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonFX", + "version": "24.2.0", + "libName": "CTRE_SimTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "24.2.0", + "libName": "CTRE_SimVictorSPX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "24.2.0", + "libName": "CTRE_SimPigeonIMU", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "24.2.0", + "libName": "CTRE_SimCANCoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "24.2.0", + "libName": "CTRE_SimProTalonFX", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "24.2.0", + "libName": "CTRE_SimProCANcoder", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "24.2.0", + "libName": "CTRE_SimProPigeon2", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/WPILibNewCommands.json b/2024-2025/Win-Nguyen/Win-Nguyen-2024/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..67bf389 --- /dev/null +++ b/2024-2025/Win-Nguyen/Win-Nguyen-2024/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": "2024", + "mavenUrls": [], + "jsonUrl": "", + "javaDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-java", + "version": "wpilib" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "edu.wpi.first.wpilibNewCommands", + "artifactId": "wpilibNewCommands-cpp", + "version": "wpilib", + "libName": "wpilibNewCommands", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "linuxarm32", + "linuxarm64", + "windowsx86-64", + "windowsx86", + "linuxx86-64", + "osxuniversal" + ] + } + ] +} From 7cc027f2c916eb1cb7d731dc08c7335a7047fd97 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Thu, 13 Feb 2025 17:49:30 -0600 Subject: [PATCH 02/32] A bunch of stuff added all at once --- .gitignore | 33 +- .../DontOverthinkIt/.gitignore | 178 ------- .../WPILib-License.md | 24 + .../build.gradle | 104 ++++ .../gradle/wrapper/gradle-wrapper.properties | 7 + .../gradlew | 252 +++++++++ .../gradlew.bat | 94 ++++ .../settings.gradle | 30 ++ .../src/main/deploy/example.txt | 3 + .../src/main/java/frc/robot/Constants.java | 27 + .../src/main/java/frc/robot/Main.java | 25 + .../src/main/java/frc/robot/Robot.java | 104 ++++ .../main/java/frc/robot/RobotContainer.java | 92 ++++ .../main/java/frc/robot/commands/Autos.java | 20 + .../frc/robot/commands/ExampleCommand.java | 63 +++ .../frc/robot/controllers/LogitechF310.java | 463 ++++++++++++++++ .../main/java/frc/robot/controllers/PS4.java | 498 ++++++++++++++++++ .../main/java/frc/robot/controllers/Xbox.java | 425 +++++++++++++++ .../frc/robot/controllers/package-info.java | 7 + .../frc/robot/subsystems/DriveSubsystem.java | 47 ++ .../robot/subsystems/ExampleSubsystem.java | 70 +++ .../vendordeps/AdvantageKit.json | 34 ++ .../vendordeps/PathplannerLib-2025.2.1.json | 38 ++ .../vendordeps/Phoenix5-replay-5.35.1.json | 221 ++++++++ .../vendordeps/Phoenix6-replay-25.2.0.json | 467 ++++++++++++++++ .../vendordeps/WPILibNewCommands.json | 38 ++ .../Henrys_Command_Robot_Test/.gitignore | 178 ------- 2024-2025/Tests/main-bot/.gitignore | 178 ------- .../Win-Nguyen/Win-Nguyen-2024/.gitignore | 178 ------- 2024-2025/main-bot/WPILib-License.md | 24 + 2024-2025/main-bot/build.gradle | 104 ++++ .../gradle/wrapper/gradle-wrapper.properties | 7 + 2024-2025/main-bot/gradlew | 252 +++++++++ 2024-2025/main-bot/gradlew.bat | 94 ++++ 2024-2025/main-bot/networktables.json | 1 + 2024-2025/main-bot/settings.gradle | 30 ++ 2024-2025/main-bot/simgui-ds.json | 92 ++++ 2024-2025/main-bot/simgui-window.json | 73 +++ 2024-2025/main-bot/simgui.json | 57 ++ .../main-bot/src/main/deploy/example.txt | 3 + .../autos/Long Curve Test Auto.auto | 19 + .../autos/Short PathPlanner Test.auto | 19 + .../src/main/deploy/pathplanner/navgrid.json | 1 + .../paths/Command Example Path.path | 70 +++ .../pathplanner/paths/Fun Short Path.path | 54 ++ .../src/main/deploy/pathplanner/settings.json | 32 ++ .../src/main/java/frc/robot/Constants.java | 46 ++ .../src/main/java/frc/robot/Main.java | 26 + .../src/main/java/frc/robot/Robot.java | 126 +++++ .../main/java/frc/robot/RobotContainer.java | 239 +++++++++ .../src/main/java/frc/robot/Telemetry.java | 125 +++++ .../src/main/java/frc/robot/Utility.java | 135 +++++ .../main/java/frc/robot/commands/Autos.java | 20 + .../frc/robot/commands/ExampleCommand.java | 43 ++ .../frc/robot/generated/TunerConstants.java | 286 ++++++++++ .../robot/subsystems/AlgaeArmSubsystem.java | 68 +++ .../subsystems/CommandSwerveDrivetrain.java | 327 ++++++++++++ .../robot/subsystems/CoralArmSubsystem.java | 108 ++++ .../robot/subsystems/ElevatorSubsystem.java | 197 +++++++ .../robot/subsystems/ExampleSubsystem.java | 47 ++ .../main-bot/vendordeps/AdvantageKit.json | 35 ++ .../vendordeps/PathplannerLib-2025.2.1.json | 38 ++ .../vendordeps/Phoenix5-replay-5.35.1.json | 221 ++++++++ .../vendordeps/Phoenix6-replay-25.2.1.json | 467 ++++++++++++++++ .../main-bot/vendordeps/Studica-2025.0.1.json | 71 +++ .../vendordeps/WPILibNewCommands.json | 38 ++ 66 files changed, 6780 insertions(+), 713 deletions(-) delete mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/WPILib-License.md create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/build.gradle create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew.bat create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/settings.gradle create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/deploy/example.txt create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Constants.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/RobotContainer.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/Autos.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/ExampleCommand.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/LogitechF310.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/PS4.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/Xbox.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/package-info.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/DriveSubsystem.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/ExampleSubsystem.java create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/AdvantageKit.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/PathplannerLib-2025.2.1.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix5-replay-5.35.1.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix6-replay-25.2.0.json create mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/WPILibNewCommands.json delete mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore delete mode 100644 2024-2025/Tests/main-bot/.gitignore delete mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore create mode 100644 2024-2025/main-bot/WPILib-License.md create mode 100644 2024-2025/main-bot/build.gradle create mode 100644 2024-2025/main-bot/gradle/wrapper/gradle-wrapper.properties create mode 100644 2024-2025/main-bot/gradlew create mode 100644 2024-2025/main-bot/gradlew.bat create mode 100644 2024-2025/main-bot/networktables.json create mode 100644 2024-2025/main-bot/settings.gradle create mode 100644 2024-2025/main-bot/simgui-ds.json create mode 100644 2024-2025/main-bot/simgui-window.json create mode 100644 2024-2025/main-bot/simgui.json create mode 100644 2024-2025/main-bot/src/main/deploy/example.txt create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/autos/Long Curve Test Auto.auto create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/autos/Short PathPlanner Test.auto create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/navgrid.json create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/paths/Command Example Path.path create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/paths/Fun Short Path.path create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/settings.json create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Constants.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Main.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Robot.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Telemetry.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Utility.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java create mode 100644 2024-2025/main-bot/vendordeps/AdvantageKit.json create mode 100644 2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json create mode 100644 2024-2025/main-bot/vendordeps/Phoenix5-replay-5.35.1.json create mode 100644 2024-2025/main-bot/vendordeps/Phoenix6-replay-25.2.1.json create mode 100644 2024-2025/main-bot/vendordeps/Studica-2025.0.1.json create mode 100644 2024-2025/main-bot/vendordeps/WPILibNewCommands.json diff --git a/.gitignore b/.gitignore index 4835bef..ce577a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # .gitignore # Ingore File For Git +*.gradle/ +*.wpilib/ +*bin/ + # -----=====[ IDEs ]=====----- # # VSCode @@ -148,9 +152,36 @@ dmypy.json # Pyre type checker .pyre/ +# -----=====[ 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* +replay_pid* + # -----=====[ Misc ]=====----- # # MacOS Finder Cache # >:( *.DS_Store - +*.dat +*.hoot diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore b/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore deleted file mode 100644 index cdbfbfa..0000000 --- a/2024-2025/FreshmanProjectBots/DontOverthinkIt/.gitignore +++ /dev/null @@ -1,178 +0,0 @@ -# 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 -*-window.json - -# Simulation data log directory -logs/ - -# Folder that has CTRE Phoenix Sim device config storage -ctre_sim/ diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/WPILib-License.md b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/build.gradle b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/build.gradle new file mode 100644 index 0000000..b9f0e50 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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 = 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 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradle/wrapper/gradle-wrapper.properties b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8e975a5 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew.bat b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/gradlew.bat new file mode 100644 index 0000000..9b42019 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/settings.gradle b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/settings.gradle new file mode 100644 index 0000000..3bc070a --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/deploy/example.txt b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Constants.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..768c0cb --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Constants.java @@ -0,0 +1,27 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package 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; + public static final int motorId = 0; + } + + public static class DriveConstants { + public static final int kFLMotorId = 3; + public static final int kFRMotorId = 1; + public static final int kBLMotorId = 4; + public static final int kBRMotorId = 2; + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Main.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..8776e5d --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Robot.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..91e75cd --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/Robot.java @@ -0,0 +1,104 @@ +// 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/RobotContainer.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..c0a0b59 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,92 @@ +// 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.Constants.DriveConstants; +import frc.robot.commands.Autos; +import frc.robot.commands.ExampleCommand; +import frc.robot.subsystems.DriveSubsystem; +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.RunCommand; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.JoystickButton; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import frc.robot.controllers.Xbox; + +/** + * 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(OperatorConstants.motorId); + private final DriveSubsystem m_driveSubsystem = new DriveSubsystem(DriveConstants.kFLMotorId, DriveConstants.kFRMotorId, DriveConstants.kBLMotorId, DriveConstants.kBRMotorId); + + // Replace with CommandPS4Controller or CommandJoystick if needed + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + + // Creation of normal controller + // private final XboxController normalXboxController = new XboxController(0); + + /** 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)); + System.out.println("Bindings Configured ********************************************"); + + // Controls using the command based xbox controller + // m_driverController.leftTrigger().whileTrue(m_driveSubsystem.setMotorVoltageCommand(m_driverController.getLeftX(), m_driverController.getRawAxis(3), m_driverController.getRawAxis(4))); + // m_driverController.rightTrigger().whileTrue(m_driveSubsystem.setMotorVoltageCommand(m_driverController.getRawAxis(2), m_driverController.getRawAxis(3), m_driverController.getRawAxis(4))); + // m_driverController.rightStick().whileTrue(m_driveSubsystem.setMotorVoltageCommand(m_driverController.getRawAxis(2), m_driverController.getRawAxis(3), m_driverController.getRawAxis(4))); + m_driveSubsystem.setDefaultCommand( + new RunCommand( + () -> + m_driveSubsystem.setMotorVoltage( + m_driverController.getLeftTriggerAxis(), + m_driverController.getRightTriggerAxis(), + m_driverController.getRightX()), + m_driveSubsystem)); + + // Controls using the normal xbox controller + // Trigger leftTriggerActivated = new JoystickButton(normalXboxController, XboxController.Axis.kLeftTrigger.value); + // leftTriggerActivated.whileTrue(m_driveSubsystem.setMotorVoltageCommand(normalXboxController.getRawAxis(2), normalXboxController.getRawAxis(3), normalXboxController.getRawAxis(4))); + + + // 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/Autos.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/ExampleCommand.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..d609d82 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/commands/ExampleCommand.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.commands; + +import frc.robot.subsystems.ExampleSubsystem; +import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard; +import edu.wpi.first.wpilibj2.command.Command; + +import com.ctre.phoenix.motorcontrol.ControlMode; +import com.ctre.phoenix.motorcontrol.TalonSRXControlMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;; + +/** An example command that uses an example subsystem. */ + + +public class ExampleCommand extends Command { + @SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"}) + private final ExampleSubsystem m_subsystem; + // private final TalonSRX FLMotor; + // private final TalonSRX BLMotor; + + + /** + * Creates a new ExampleCommand. + * + * @param subsystem The subsystem used by this command. + */ + public ExampleCommand(ExampleSubsystem subsystem ) { + m_subsystem = subsystem; + // FLMotor = new TalonSRX(FLId); + // BLMotor = new TalonSRX(BLId); + // 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() { + + // double leftFrontMotorInput = rightTrigger + jfioejo + hiofeaj; + + // FLMotor.set(ControlMode.PercentOutput, leftFrontMotorInput); + // BLMotor.set(ControlMode.PercentOutput, leftTrigger); + } + + // 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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/LogitechF310.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/LogitechF310.java new file mode 100644 index 0000000..57ba088 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/LogitechF310.java @@ -0,0 +1,463 @@ +package frc.robot.controllers; + +import edu.wpi.first.wpilibj.Joystick; + +/** + * This class is used to create an LogitechF310 controller object. + *

+ * This class uses the {@code Joystick} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see PS4 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.Joystick + */ +public class LogitechF310 { + + // Button IDs // + + /** + * The axis ID for the left joystick x. + */ + private static final int JOYSTICK_LEFT_X = 0; + + /** + * The axis ID for the left joystick y. + */ + private static final int JOYSTICK_LEFT_Y = 1; + + /** + * The axis ID for the left trigger. + */ + private static final int TRIGGER_LEFT = 2; + + /** + * The axis ID for the right trigger. + */ + private static final int TRIGGER_RIGHT = 3; + + /** + * The axis ID for the right joystick x. + */ + private static final int JOYSTICK_RIGHT_X = 4; + + /** + * The axis ID for the right joystick y. + */ + private static final int JOYSTICK_RIGHT_Y = 5; + + /** + * The button ID for the A button. + */ + private static final int BUTTON_A = 1; + + /** + * The button ID for the B button. + */ + private static final int BUTTON_B = 2; + + /** + * The button ID for the X button. + */ + private static final int BUTTON_X = 3; + + /** + * The button ID for the Y button. + */ + private static final int BUTTON_Y = 4; + + /** + * The button ID for the left bumper. + */ + private static final int BUTTON_LEFT_BUMPER = 5; + + /** + * The button ID for the right bumper. + */ + private static final int BUTTON_RIGHT_BUMPER = 6; + + /** + * The button ID for the start button. + */ + private static final int BUTTON_START = 7; + + /** + * The button ID for the back button. + */ + private static final int BUTTON_BACK = 8; + + /** + * The controller object. + * In this case, a {@code Joystick} object. + * + * @see edu.wpi.first.wpilibj.Joystick + */ + private Joystick controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the LogitechF310 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public LogitechF310(final int id, final double deadBandValue) { + controller = new Joystick(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_X)); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getRawAxis(JOYSTICK_LEFT_Y)); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see LogitechF310#getRightY + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_X)); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see LogitechF310#getRightX + * @see LogitechF310#getLeftX + * @see LogitechF310#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRawAxis(JOYSTICK_RIGHT_Y)); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return Value of the left trigger + * + * @see LogitechF310#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_LEFT)); + } + + /** + * Method for returning the value of the right trigger. + * + * @return Value of the right trigger + * + * @see LogitechF310#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getRawAxis(TRIGGER_RIGHT)); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return Value of the A button + * + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getAButton() { + return controller.getRawButton(BUTTON_A); + } + + /** + * Method for returning the value of the B button. + * + * @return Value of the B button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getXButton + * @see LogitechF310#getYButton + */ + public boolean getBButton() { + return controller.getRawButton(BUTTON_B); + } + + /** + * Method for returning the value of the X button. + * + * @return Value of the X button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getYButton + */ + public boolean getXButton() { + return controller.getRawButton(BUTTON_X); + } + + /** + * Method for returning the value of the Y button. + * + * @return Value of the Y button + * + * @see LogitechF310#getAButton + * @see LogitechF310#getBButton + * @see LogitechF310#getXButton + */ + public boolean getYButton() { + return controller.getRawButton(BUTTON_Y); + } + + /** + * Method for returning the value of the back button. + * + * @return Value of the back button + * + * @see LogitechF310#getStartButton + */ + public boolean getBackButton() { + return controller.getRawButton(BUTTON_START); + } + + /** + * Method for returning the value of the start button. + * + * @return Value of the start button + * + * @see LogitechF310#getBackButton + */ + public boolean getStartButton() { + return controller.getRawButton(BUTTON_BACK); + } + + /** + * Method for returning the value of the left bumper. + * + * @return Value of the left bumper + * + * @see LogitechF310#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getRawButton(BUTTON_LEFT_BUMPER); + } + + /** + * Method for returning the value of the right bumper. + * + * @return Value of the right bumper + * + * @see LogitechF310#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRawButton(BUTTON_RIGHT_BUMPER); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see LogitechF310.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + // This controller does not have rumble functionality :( + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/PS4.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/PS4.java new file mode 100644 index 0000000..398da6c --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/PS4.java @@ -0,0 +1,498 @@ +package frc.robot.controllers; + +import edu.wpi.first.wpilibj.PS4Controller; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an PS4 controller object. + *

+ * This class uses the {@code PS4Controller} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see Xbox + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ +public class PS4 { + + /** + * The controller object. + * In this case, a {@code PS4Controller} object. + * + * @see edu.wpi.first.wpilibj.PS4Controller + */ + private PS4Controller controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the PS4 class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public PS4(final int id, final double deadBandValue) { + controller = new PS4Controller(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see PS4#getRightX + * @see PS4#getRightY + * @see PS4#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see PS4#getLeftX + * @see PS4#getRightY + * @see PS4#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see PS4#getRightX + * @see PS4#getLeftX + * @see PS4#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see PS4#getRightTrigger + */ + public double getLeftTrigger() { + return deadBand(controller.getL2Axis()); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see PS4#getLeftTrigger + */ + public double getRightTrigger() { + return deadBand(controller.getR2Axis()); + } + + // Button Methods // + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getXButton() { + return controller.getCrossButton(); + } + + /** + * Method for returning the value of the Square button. + * + * @return value of the Square button + * + * @see PS4#getXButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getSquareButton() { + return controller.getSquareButton(); + } + + /** + * Method for returning the value of the Circle button. + * + * @return value of the Circle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getCircleButton() { + return controller.getCircleButton(); + } + + /** + * Method for returning the value of the Triangle button. + * + * @return value of the Triangle button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getTriangleButton() { + return controller.getTriangleButton(); + } + + /** + * Method for returning the value of the Share button. + * + * @return value of the Share button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getShareButton() { + return controller.getShareButton(); + } + + /** + * Method for returning the value of the Options button. + * + * @return value of the Options button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getPlayStationButton + * @see PS4#getTouchpadButton + */ + public boolean getOptionsButton() { + return controller.getOptionsButton(); + } + + /** + * Method for returning the value of the PlayStation button. + * + * @return value of the PlayStation button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getTouchpadButton + */ + public boolean getPlayStationButton() { + return controller.getPSButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see PS4#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getL1Button(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see PS4#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getR1Button(); + } + + /** + * Method for returning the value of the left joystick button. + * + * @return value of the left joystick button + * + * @see PS4#getRightJoystickButton + */ + public boolean getLeftJoystickButton() { + return controller.getL3Button(); + } + + /** + * Method for returning the value of the right joystick button. + * + * @return value of the right joystick button + * + * @see PS4#getLeftJoystickButton + */ + public boolean getRightJoystickButton() { + return controller.getR3Button(); + } + + // Touchpad Methods // + + /** + * Method for returning the value of the Touchpad button. + * + * @return value of the Touchpad button + * + * @see PS4#getXButton + * @see PS4#getSquareButton + * @see PS4#getCircleButton + * @see PS4#getTriangleButton + * @see PS4#getShareButton + * @see PS4#getOptionsButton + * @see PS4#getPlayStationButton + */ + public boolean getTouchpadButton() { + return controller.getTouchpad(); + } + + // There is a 'touchpad' that uses an event loop + // but for the purpose of this class, it is not needed + // It might be added in a future version if there becomes a need for it + // If you have a need for it or have added it yourself, + // Submit a pull request or submit an issue on the GitHub repository + // https://github.com/J-The-Fox/FRC-Team-5098 + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see PS4.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/Xbox.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/Xbox.java new file mode 100644 index 0000000..421866a --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/Xbox.java @@ -0,0 +1,425 @@ +package frc.robot.controllers; + +import edu.wpi.first.wpilibj.XboxController; +import edu.wpi.first.wpilibj.GenericHID.RumbleType; + +/** + * This class is used to create an Xbox controller object. + *

+ * This class uses the {@code XboxController} class from the wpilibj library. + *

+ *

+ * This uses a deadband that can be set if needed. This helps control drift. + * This value can be in a range of 0 to 1. The higher the value, the more the + * stick has to move to register. This value is set in the constructor and is + * set alongside the controller ID. As a note: The deadband is only applied + * to the joystick axes. + *

+ * + * @see LogitechF310 + * @see PS4 + * + * @see edu.wpi.first.wpilibj.XboxController + */ +public class Xbox { + + /** + * The controller object. + * In this case, a {@code XboxController} object. + */ + private XboxController controller; + + /** + * The deadband value. + */ + private double deadBand; + + /** + * Constructor for the Xbox class. + *

+ * This constructor is used to create a LogitechF310 object. + * This takes in the controller ID and the deadband value. + *

+ * + * @param id Controller ID (Port) + * @param deadBandValue Deadband value + */ + public Xbox(final int id, final double deadBandValue) { + controller = new XboxController(id); + deadBand = deadBandValue; + } + + // Deadband Method // + + // Although this isn't really needed for newer controllers, + // it's still here just in case one might develop drift + + /** + * Method for setting the deadband of the controller on the X and Y axes. + *

+ * This sets a threshold the joystick must pass before registering a value. + * This value can be in a range of 0 to 1. The higher the value, + * the more the joystick has to move to register. + *

+ * + * @return Deadbanded value + * + * @param value Value to be deadbanded + */ + private double deadBand(final double value) { + return Math.abs(value) < deadBand ? 0 : value; + } + + // Joystick Methods // + + /** + * Method for returning the X value of the left joystick. + * + * @return X value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftY + */ + public double getLeftX() { + return deadBand(controller.getLeftX()); + } + + /** + * Method for returning the Y value of the left joystick. + * + * @return Y value of the left joystick + * + * @see Xbox#getRightX + * @see Xbox#getRightY + * @see Xbox#getLeftX + */ + public double getLeftY() { + return deadBand(controller.getLeftY()); + } + + /** + * Method for returning the X value of the right joystick. + * + * @return X value of the right joystick + * + * @see Xbox#getRightY + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightX() { + return deadBand(controller.getRightX()); + } + + /** + * Method for returning the Y value of the right joystick. + * + * @return Y value of the right joystick + * + * @see Xbox#getRightX + * @see Xbox#getLeftX + * @see Xbox#getLeftY + */ + public double getRightY() { + return deadBand(controller.getRightY()); + } + + // Trigger Methods // + + /** + * Method for returning the value of the left trigger. + * + * @return value of the left trigger + * + * @see Xbox#getRightTrigger + */ + public double getLeftTrigger() { + return controller.getLeftTriggerAxis(); + } + + /** + * Method for returning the value of the right trigger. + * + * @return value of the right trigger + * + * @see Xbox#getLeftTrigger + */ + public double getRightTrigger() { + return controller.getRightTriggerAxis(); + } + + // Button Methods // + + /** + * Method for returning the value of the A button. + * + * @return value of the A button + * + * @see Xbox#getBButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getAButton() { + return controller.getAButton(); + } + + /** + * Method for returning the value of the B button. + * + * @return value of the B button + * + * @see Xbox#getAButton + * @see Xbox#getXButton + * @see Xbox#getYButton + */ + public boolean getBButton() { + return controller.getBButton(); + } + + /** + * Method for returning the value of the X button. + * + * @return value of the X button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getYButton + */ + public boolean getXButton() { + return controller.getXButton(); + } + + /** + * Method for returning the value of the Y button. + * + * @return value of the Y button + * + * @see Xbox#getAButton + * @see Xbox#getBButton + * @see Xbox#getXButton + */ + public boolean getYButton() { + return controller.getYButton(); + } + + /** + * Method for returning the value of the start button. + * + * @return value of the start button + * + * @see Xbox#getBackButton + */ + public boolean getStartButton() { + return controller.getStartButton(); + } + + /** + * Method for returning the value of the back button. + * + * @return value of the back button + * + * @see Xbox#getStartButton + */ + public boolean getBackButton() { + return controller.getBackButton(); + } + + /** + * Method for returning the value of the left bumper. + * + * @return value of the left bumper + * + * @see Xbox#getRightBumper + */ + public boolean getLeftBumper() { + return controller.getLeftBumper(); + } + + /** + * Method for returning the value of the right bumper. + * + * @return value of the right bumper + * + * @see Xbox#getLeftBumper + */ + public boolean getRightBumper() { + return controller.getRightBumper(); + } + + /** + * Method for returning the value of the left stick button. + * + * @return value of the left stick button + * + * @see Xbox#getRightStickButton + */ + public boolean getLeftStickButton() { + return controller.getLeftStickButton(); + } + + /** + * Method for returning the value of the right stick button. + * + * @return value of the right stick button + * + * @see Xbox#getLeftStickButton + */ + public boolean getRightStickButton() { + return controller.getRightStickButton(); + } + + // D-Pad Methods // + + public enum DPad { + /** + * Up on the D-Pad. + */ + up, + + /** + * Down on the D-Pad. + */ + down, + + /** + * Right on the D-Pad. + */ + left, + + /** + * Left on the D-Pad. + */ + right, + + /** + * Up-Right on the D-Pad. + */ + upright, + + /** + * Up-Left on the D-Pad. + */ + upleft, + + /** + * Down-Right on the D-Pad. + */ + downright, + + /** + * Down-Left on the D-Pad. + */ + downleft, + + /** + * No direction on the D-Pad. + */ + none + } + + /** + * Upright D-Pad POV value. + */ + private static final int DPAD_UPRIGHT = 45; + + /** + * Right D-Pad POV value. + */ + private static final int DPAD_RIGHT = 90; + + /** + * Downright D-Pad POV value. + */ + private static final int DPAD_DOWNRIGHT = 135; + + /** + * Down POV D-Pad value. + */ + private static final int DPAD_DOWN = 180; + + /** + * Downleft D-Pad POV value. + */ + private static final int DPAD_DOWNLEFT = 225; + + /** + * Left D-Pad POV value. + */ + private static final int DPAD_LEFT = 270; + + /** + * Upleft D-Pad POV value. + */ + private static final int DPAD_UPLEFT = 315; + + /** + * Up D-Pad POV value. + */ + private static final int DPAD_UP = 360; + + /** + * Method for returning the value of the D-Pad. + * + * @return value of the D-Pad + * + * @see Xbox.DPad + */ + public DPad getDPad() { + switch (controller.getPOV()) { + case 0: + return DPad.up; + case DPAD_UPRIGHT: + return DPad.upright; + case DPAD_RIGHT: + return DPad.right; + case DPAD_DOWNRIGHT: + return DPad.downright; + case DPAD_DOWN: + return DPad.down; + case DPAD_DOWNLEFT: + return DPad.downleft; + case DPAD_LEFT: + return DPad.left; + case DPAD_UPLEFT: + return DPad.upleft; + case DPAD_UP: + return DPad.up; + default: + return DPad.none; + } + } + + // Rumble Methods // + + /** + * Method for setting the rumble of the controller. + * + * @param type type of rumble to set + * @param value value to set the rumble to + * + * @see edu.wpi.first.wpilibj.GenericHID.RumbleType + */ + public void setRumble(final RumbleType type, final double value) { + controller.setRumble(type, value); + } + + // General Methods // + + /** + * Method for checking if the controller is connected. + * + * @return true if the controller is connected, false if not + */ + public boolean isConnected() { + return (controller.isConnected()); + } + + /** + * Method for getting the name of the controller. + * + * @return name of the controller + */ + public String getName() { + return controller.getName(); + } +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/package-info.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/package-info.java new file mode 100644 index 0000000..7c768d9 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/controllers/package-info.java @@ -0,0 +1,7 @@ +/** + * Holds controller classes + *

+ * This can be used to hold both custom and pre-made controller classes. + *

+ */ +package frc.robot.controllers; diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/DriveSubsystem.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/DriveSubsystem.java new file mode 100644 index 0000000..6877d47 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/DriveSubsystem.java @@ -0,0 +1,47 @@ +package frc.robot.subsystems; + +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Command; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import com.ctre.phoenix.motorcontrol.ControlMode; + +public class DriveSubsystem extends SubsystemBase { + + // Motors + private final TalonSRX FLMotor; + private final TalonSRX FRMotor; + private final TalonSRX BLMotor; + private final TalonSRX BRMotor; + + public DriveSubsystem(int FLId, int FRId, int BLId, int BRId) { + FLMotor = new TalonSRX(FLId); + FRMotor = new TalonSRX(FRId); + BLMotor = new TalonSRX(BLId); + BRMotor = new TalonSRX(BRId); + } + + public Command setMotorVoltageCommand(double leftTrigger, double rightTrigger, double rightXAxis) { + return runOnce(() -> { + // System.out.println("Command Ran"); + setMotorVoltage(leftTrigger, rightTrigger, rightXAxis); + }); + } + + public void setMotorVoltage(double leftTrigger, double rightTrigger, double rightXAxis) { + // System.out.println("method ran"); + FLMotor.set(ControlMode.PercentOutput, rightTrigger -leftTrigger + rightXAxis); + FRMotor.set(ControlMode.PercentOutput, -rightTrigger + leftTrigger -rightXAxis); + BLMotor.set(ControlMode.PercentOutput, rightTrigger -leftTrigger + rightXAxis); + BRMotor.set(ControlMode.PercentOutput, -rightTrigger + leftTrigger -rightXAxis); + + // FLMotor.set(ControlMode.PercentOutput, 0.5); + // BLMotor.set(ControlMode.PercentOutput, 0.5); + + SmartDashboard.putNumber("Left Trigger", leftTrigger); + SmartDashboard.putNumber("Right Trigger", rightTrigger); + SmartDashboard.putNumber("Stick", rightXAxis); + + } + +} diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..53776e3 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/src/main/java/frc/robot/subsystems/ExampleSubsystem.java @@ -0,0 +1,70 @@ +// 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; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import com.ctre.phoenix.motorcontrol.ControlMode; + +public class ExampleSubsystem extends SubsystemBase { + + private final TalonSRX testMotor; + /** Creates a new ExampleSubsystem. */ + public ExampleSubsystem(int testId) { + testMotor = new TalonSRX(testId); + } + + /** + * 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 */ + }); + } + + public Command setMotorVoltageCommand(double voltage) { + return runOnce(() + -> { + setMotorVoltage(voltage); + }); + } + + /** + * 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. + int x = 3; + if (x != 1) { + return false; + } + return true; + + } + + public void setMotorVoltage(double voltage) { + testMotor.set(ControlMode.PercentOutput, voltage); + } + + @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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/AdvantageKit.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/AdvantageKit.json new file mode 100644 index 0000000..1fa7c03 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/AdvantageKit.json @@ -0,0 +1,34 @@ +{ + "fileName": "AdvantageKit.json", + "name": "AdvantageKit", + "version": "4.0.0", + "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", + "frcYear": "2025", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], + "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", + "javaDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-java", + "version": "4.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-wpilibio", + "version": "4.0.0", + "skipInvalidPlatforms": false, + "isJar": false, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64", + "osxuniversal" + ] + } + ], + "cppDependencies": [] +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/PathplannerLib-2025.2.1.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/PathplannerLib-2025.2.1.json new file mode 100644 index 0000000..71e25f3 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/PathplannerLib-2025.2.1.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2025.2.1.json", + "name": "PathplannerLib", + "version": "2025.2.1", + "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.1" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2025.2.1", + "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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix5-replay-5.35.1.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix5-replay-5.35.1.json new file mode 100644 index 0000000..bdf2f44 --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix5-replay-5.35.1.json @@ -0,0 +1,221 @@ +{ + "fileName": "Phoenix5-replay-5.35.1.json", + "name": "CTRE-Phoenix (v5)", + "version": "5.35.1", + "frcYear": "2025", + "uuid": "fbc886a4-2cec-40c0-9835-71086a8cc3df", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-replay-frc2025-latest.json", + "requires": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Phoenix 5 requires low-level libraries from Phoenix 6. Please add the Phoenix 6 vendordep before adding Phoenix 5.", + "offlineFileName": "Phoenix6-replay-frc2025-latest.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-replay-frc2025-latest.json" + } + ], + "conflictsWith": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Users must use the regular Phoenix 5 vendordep when using the regular Phoenix 6 vendordep.", + "offlineFileName": "Phoenix6-frc2025-latest.json" + }, + { + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "errorMessage": "Users cannot have both the replay and regular Phoenix 5 vendordeps in their robot program.", + "offlineFileName": "Phoenix5-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.35.1" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.35.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "cci-replay", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "wpiapi-cpp-replay", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "api-cpp-replay", + "version": "5.35.1", + "libName": "CTRE_PhoenixReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "cci-replay", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "api-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixSim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix6-replay-25.2.0.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix6-replay-25.2.0.json new file mode 100644 index 0000000..52725fb --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/Phoenix6-replay-25.2.0.json @@ -0,0 +1,467 @@ +{ + "fileName": "Phoenix6-replay-25.2.0.json", + "name": "CTRE-Phoenix (v6) Replay", + "version": "25.2.0", + "frcYear": "2025", + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-replay-frc2025-latest.json", + "conflictsWith": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "25.2.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "api-cpp-replay", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "tools-replay", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.2.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "25.2.0", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.0", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "wpiapi-cpp-replay", + "version": "25.2.0", + "libName": "CTRE_Phoenix6_WPIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "tools-replay", + "version": "25.2.0", + "libName": "CTRE_PhoenixTools_Replay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "25.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.0", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/WPILibNewCommands.json b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..3718e0a --- /dev/null +++ b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test-Imported/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/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore deleted file mode 100644 index cdbfbfa..0000000 --- a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/.gitignore +++ /dev/null @@ -1,178 +0,0 @@ -# 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 -*-window.json - -# Simulation data log directory -logs/ - -# Folder that has CTRE Phoenix Sim device config storage -ctre_sim/ diff --git a/2024-2025/Tests/main-bot/.gitignore b/2024-2025/Tests/main-bot/.gitignore deleted file mode 100644 index cdbfbfa..0000000 --- a/2024-2025/Tests/main-bot/.gitignore +++ /dev/null @@ -1,178 +0,0 @@ -# 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 -*-window.json - -# Simulation data log directory -logs/ - -# Folder that has CTRE Phoenix Sim device config storage -ctre_sim/ diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore b/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore deleted file mode 100644 index cdbfbfa..0000000 --- a/2024-2025/Win-Nguyen/Win-Nguyen-2024/.gitignore +++ /dev/null @@ -1,178 +0,0 @@ -# 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 -*-window.json - -# Simulation data log directory -logs/ - -# Folder that has CTRE Phoenix Sim device config storage -ctre_sim/ diff --git a/2024-2025/main-bot/WPILib-License.md b/2024-2025/main-bot/WPILib-License.md new file mode 100644 index 0000000..e7cd597 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/build.gradle b/2024-2025/main-bot/build.gradle new file mode 100644 index 0000000..b15eee5 --- /dev/null +++ b/2024-2025/main-bot/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 = 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 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/2024-2025/main-bot/gradle/wrapper/gradle-wrapper.properties b/2024-2025/main-bot/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8e975a5 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/gradlew b/2024-2025/main-bot/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/gradlew.bat b/2024-2025/main-bot/gradlew.bat new file mode 100644 index 0000000..9b42019 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/networktables.json b/2024-2025/main-bot/networktables.json new file mode 100644 index 0000000..60b0742 --- /dev/null +++ b/2024-2025/main-bot/networktables.json @@ -0,0 +1 @@ +[] diff --git a/2024-2025/main-bot/settings.gradle b/2024-2025/main-bot/settings.gradle new file mode 100644 index 0000000..3bc070a --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/simgui-ds.json b/2024-2025/main-bot/simgui-ds.json new file mode 100644 index 0000000..73cc713 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/simgui-window.json b/2024-2025/main-bot/simgui-window.json new file mode 100644 index 0000000..391c753 --- /dev/null +++ b/2024-2025/main-bot/simgui-window.json @@ -0,0 +1,73 @@ +{ + "Docking": { + "Data": [] + }, + "MainWindow": { + "GLOBAL": { + "font": "Proggy Dotted", + "fps": "120", + "height": "720", + "maximized": "0", + "style": "0", + "userScale": "2", + "width": "1280", + "xpos": "0", + "ypos": "23" + } + }, + "Table": { + "0xE56EC1C2,4": { + "Column 0 Weight": "1.0000", + "Column 1 Weight": "1.0000", + "Column 2 Weight": "1.0000", + "Column 3 Weight": "1.0000" + } + }, + "Window": { + "###FMS": { + "Collapsed": "0", + "Pos": "17,595", + "Size": "283,140" + }, + "###Joysticks": { + "Collapsed": "0", + "Pos": "262,701", + "Size": "796,73" + }, + "###NetworkTables": { + "Collapsed": "1", + "Pos": "250,277", + "Size": "750,440" + }, + "###NetworkTables Info": { + "Collapsed": "0", + "Pos": "250,130", + "Size": "750,145" + }, + "###Other Devices": { + "Collapsed": "0", + "Pos": "1261,42", + "Size": "250,695" + }, + "###System Joysticks": { + "Collapsed": "0", + "Pos": "12,348", + "Size": "192,218" + }, + "###Timing": { + "Collapsed": "0", + "Pos": "5,150", + "Size": "135,173" + }, + "Debug##Default": { + "Collapsed": "0", + "Pos": "60,60", + "Size": "400,400" + }, + "Robot State": { + "Collapsed": "0", + "Pos": "5,20", + "Size": "99,116" + } + } +} diff --git a/2024-2025/main-bot/simgui.json b/2024-2025/main-bot/simgui.json new file mode 100644 index 0000000..322d8a3 --- /dev/null +++ b/2024-2025/main-bot/simgui.json @@ -0,0 +1,57 @@ +{ + "HALProvider": { + "Other Devices": { + "Talon FX (v6)[13]": { + "header": { + "open": true + } + } + } + }, + "NTProvider": { + "types": { + "/FMSInfo": "FMSInfo", + "/Pose": "Field2d", + "/SmartDashboard/Alerts": "Alerts", + "/SmartDashboard/Auto Chooser": "String Chooser", + "/SmartDashboard/Module 0": "Mechanism2d", + "/SmartDashboard/Module 1": "Mechanism2d", + "/SmartDashboard/Module 2": "Mechanism2d", + "/SmartDashboard/Module 3": "Mechanism2d" + } + }, + "NetworkTables": { + "transitory": { + "DriveState": { + "open": true + }, + "SmartDashboard": { + "Alerts": { + "open": true + }, + "Auto Chooser": { + "open": true + }, + "Module 0": { + "open": true + }, + "Module 1": { + "open": true + }, + "Module 2": { + "open": true + }, + "Module 3": { + "open": true + }, + "open": true + } + } + }, + "NetworkTables Info": { + "Clients": { + "open": true + }, + "visible": true + } +} diff --git a/2024-2025/main-bot/src/main/deploy/example.txt b/2024-2025/main-bot/src/main/deploy/example.txt new file mode 100644 index 0000000..bb82515 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Long Curve Test Auto.auto b/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Long Curve Test Auto.auto new file mode 100644 index 0000000..11e4ee9 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Long Curve Test Auto.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "Command Example Path" + } + } + ] + } + }, + "resetOdom": true, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Short PathPlanner Test.auto b/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Short PathPlanner Test.auto new file mode 100644 index 0000000..f94ce95 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/autos/Short PathPlanner Test.auto @@ -0,0 +1,19 @@ +{ + "version": "2025.0", + "command": { + "type": "sequential", + "data": { + "commands": [ + { + "type": "path", + "data": { + "pathName": "Fun Short Path" + } + } + ] + } + }, + "resetOdom": true, + "folder": null, + "choreoAuto": false +} \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/deploy/pathplanner/navgrid.json b/2024-2025/main-bot/src/main/deploy/pathplanner/navgrid.json new file mode 100644 index 0000000..23e0db9 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Command Example Path.path b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Command Example Path.path new file mode 100644 index 0000000..cd58fd4 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Command Example Path.path @@ -0,0 +1,70 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 7.0 + }, + "prevControl": null, + "nextControl": { + "x": 3.0, + "y": 7.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 6.58125, + "y": 6.74464897260274 + }, + "prevControl": { + "x": 5.860809075342465, + "y": 7.706271404109589 + }, + "nextControl": { + "x": 7.301690924657534, + "y": 5.783026541095891 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 6.881763698630137, + "y": 3.1535102739726026 + }, + "prevControl": { + "x": 6.971917808219178, + "y": 4.235359589041096 + }, + "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/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Fun Short Path.path b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Fun Short Path.path new file mode 100644 index 0000000..95b3ae8 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/Fun Short Path.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 7.0 + }, + "prevControl": null, + "nextControl": { + "x": 3.0, + "y": 7.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 4.1470890410958905, + "y": 5.587671232876712 + }, + "prevControl": { + "x": 3.1470890410958905, + "y": 5.587671232876712 + }, + "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": 178.8622775462112 + }, + "reversed": false, + "folder": null, + "idealStartingState": { + "velocity": 0, + "rotation": 0.0 + }, + "useDefaultConstraints": true +} \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/deploy/pathplanner/settings.json b/2024-2025/main-bot/src/main/deploy/pathplanner/settings.json new file mode 100644 index 0000000..1ce63a6 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/settings.json @@ -0,0 +1,32 @@ +{ + "robotWidth": 0.9, + "robotLength": 0.9, + "holonomicMode": true, + "pathFolders": [], + "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.051, + "driveGearing": 5.143, + "maxDriveSpeed": 5.45, + "driveMotorType": "krakenX60", + "driveCurrentLimit": 60.0, + "wheelCOF": 1.2, + "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/2024-2025/main-bot/src/main/java/frc/robot/Constants.java b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java new file mode 100644 index 0000000..809d1d2 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java @@ -0,0 +1,46 @@ +// 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; + public static final int kAuxiliaryControllerPort = 1; + } + // PLACEHOLDER IDS!!! + public static class ElevatorConstants { + public static final int kElevatorLeftMotorPort = 12; + public static final int kElevatorRightMotorPort = 13; + public static final double kMotorElevatorSpeed = 0.2; + public static final int kElevatorEncoderBottomValue = 0; + public static final int kElevatorEncoderTopValue = 1000; + + public static class ElevatorPreset { + // PLACEHOLDER VALUES!!! + public static final double level1EncoderValue = 0.5; + public static final double level2EncoderValue = 1.0; + public static final double level3EncoderValue = 1.5; + public static final double level4EncoderValue = 2.0; + } + } + public static final class AlgaeArmConstants { + public static final int algaeArmBarID = 8;//placeholder + } + public static class CoralArmConstants { + public static final int coralArmID = 69;//placeholder with funny number hehe + public static final int kCoralEncoderTopValue = 1000; + public static final int kCoralEncoderTopBuffer = kCoralEncoderTopValue + 10; + public static final int kCoralEncoderBottomValue = 0; + public static final int kCoralEncoderBottomBuffer = kCoralEncoderBottomValue - 10; + } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Main.java b/2024-2025/main-bot/src/main/java/frc/robot/Main.java new file mode 100644 index 0000000..6631d14 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Main.java @@ -0,0 +1,26 @@ +// 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. + * Man it sure is nice to add secret comments for others to find as easter eggs :O + */ + public static void main(String[] args) { + RobotBase.startRobot(Robot::new); + } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java new file mode 100644 index 0000000..f43ffad --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java @@ -0,0 +1,126 @@ +// PROGRAMMING TO DOS! + +// Basic TO DOs + // Set up Elevator simulation or Algae arm simulation in test bed // Status: Failed + +// Advanced TO DOs + //NONE + +// To-Dos: Once the elevator is attached + // Move elevator and update encoder preset values in Constants.java // Status: Not Started + // Check which motors need to be reversed// Status: Not Started + // Investigate Motion Magic // Status: Not Started + + + + + + + + + + +// 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 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; + + /** + * This function is run when the robot is first started up and should be used for any + * initialization code. + */ + public Robot() {//instantiate robot container + + 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() {} +} \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java new file mode 100644 index 0000000..bc366ed --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -0,0 +1,239 @@ +// 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. + +/* + * This Is The Input List. Whenever Adding A New Input, Make Sure To Add It To The List Below + * + * Driver Controller: + * A - Brake + * B - Point + * Back + Y - SysId Dynamic Forward + * Back + X - SysId Dynamic Reverse + * Start + Y - SysId Quasistatic Forward + * Start + X - SysId Quasistatic Reverse + * DPad Up - Coral Arm Up + * DPad Down - Coral Arm Down + * DPad Center - Coral Arm Stop + * Right Stick - Coral Arm Emergency Stop + * LB - Reset Field Centric Seed + * + * Auxiliary Controller: + * A - Algae Bar Spin Fast + * B - Algae Bar Spit Out + * Y - Algae Bar Spin Slow + * LB - Elevator Level 1 + * RB - Elevator Level 2 + * LT - Elevator Level 3 + */ + +package frc.robot; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.swerve.SwerveModule.DriveRequestType; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.auto.NamedCommands; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.ctre.phoenix6.swerve.SwerveRequest; +import java.lang.Math; +import frc.robot.Constants.OperatorConstants; +import frc.robot.generated.TunerConstants; +import frc.robot.commands.Autos; +import frc.robot.commands.ExampleCommand; +import frc.robot.subsystems.ElevatorSubsystem; +import frc.robot.subsystems.ExampleSubsystem; +import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; +import frc.robot.subsystems.ElevatorSubsystem.ElevatorLevels; +import frc.robot.subsystems.AlgaeArmSubsystem; +import frc.robot.subsystems.CommandSwerveDrivetrain; +import frc.robot.subsystems.CoralArmSubsystem; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.button.CommandGenericHID; +import edu.wpi.first.wpilibj2.command.button.Trigger; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; + +/** little secret comment OwO + * 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(); + private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( + Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem(Constants.AlgaeArmConstants.algaeArmBarID); + private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); + + private final SendableChooser autoChooser; + /* Swerve drive platform setup */ // From Swerve Project Generator + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max angular velocity + + /* Setting up bindings for necessary control of the swerve drive platform */ + private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric() + .withDeadband(MaxSpeed * 0.1).withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband + .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors + private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); + private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); + + private final Telemetry logger = new Telemetry(MaxSpeed); + + private final CommandXboxController m_auxillaryController = new CommandXboxController(Constants.OperatorConstants.kAuxiliaryControllerPort); + + public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + + // End of Swerve Drive Platform setup + + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + + + // Build an auto chooser. This will use Commands.none() as the default option. + autoChooser = AutoBuilder.buildAutoChooser(); + + // Another option that allows you to specify the default auto by its name + // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); + + SmartDashboard.putData("Auto Chooser", autoChooser); + + // Register named commands for use in autonomous routines + // NamedCommands.registerCommand("Example Command", m_exampleSubsystem.exampleMethodCommand()); + // NamedCommands.registerCommand("MoveElevatorToPresets", m_elevatorSubsystem.commandMoveToPreset(null)); + + // Configure the trigger bindings + configureBindings(Math.random()); + } + + /** + * 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}. + * @param random TODO + */ + private void configureBindings(double random) { + + // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is pressed, + // cancelling on release. + // m_auxiliaryController.axisLessThan(2, .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about holding it in place? + // m_auxiliaryController.axisGreaterThan(3, .4).whileTrue(m_elevatorSubsystem.raiseElevator()); + + + // Elevator Subsystem + //The commands below make the elevator go to certain levels + m_auxillaryController.leftBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left bumper pressed, move to level 1. Else, go to top + m_auxillaryController.rightBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If right bumper pressed, move to level 2. Else, go to top + m_auxillaryController.leftTrigger().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left trigger pressed, move to level 3. Else, go to top + + // Algae Arm Subsystem + m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop + m_auxillaryController.b().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop + m_auxillaryController.y().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop + //m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); + //This command was taken out due to clashing and we already have a command that does the same thing + + // Command Compositions for Elevator + Coral + m_auxillaryController.a().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.b().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.y().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.x().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + + // Coral Arm Subsystem + m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));//Makes Coral Arm Go Up + m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));//Makes Coral Arm Go Down + m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));//Stops Coral Arm + m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());//Emergency Stop for Coral Arm(in case it goes past the top or bottom) Note: I don't know if the onTrue method will only run once, so test it before you use it + + + // Drive Subsystem + // Code below is from swerve drive project generator + + // Note that X is defined as forward according to WPILib convention, + // and Y is defined as to the left according to WPILib convention. + drivetrain.setDefaultCommand( + // Drivetrain will execute this command periodically + drivetrain.applyRequest(() -> + drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward with negative Y (forward) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with negative X (left) + ) + ); + m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); + m_driverController.b().whileTrue(drivetrain.applyRequest(() -> + point.withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())) + )); + + // Run SysId routines when holding back/start and X/Y. + // Note that each routine should be run exactly once in a single log. + m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + + // reset the field-centric heading on left bumper press + m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + + drivetrain.registerTelemetry(logger::telemeterize); + + + } + + /** + * 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); + return autoChooser.getSelected(); + } +} +//another why not comment :) +// I do love some good comments :D \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Telemetry.java b/2024-2025/main-bot/src/main/java/frc/robot/Telemetry.java new file mode 100644 index 0000000..d00e7b5 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Telemetry.java @@ -0,0 +1,125 @@ +package frc.robot; + +import com.ctre.phoenix6.SignalLogger; +import com.ctre.phoenix6.swerve.SwerveDrivetrain.SwerveDriveState; + +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.ChassisSpeeds; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.math.kinematics.SwerveModuleState; +import edu.wpi.first.networktables.DoubleArrayPublisher; +import edu.wpi.first.networktables.DoublePublisher; +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.networktables.StringPublisher; +import edu.wpi.first.networktables.StructArrayPublisher; +import edu.wpi.first.networktables.StructPublisher; +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; + +public class Telemetry { + private final double MaxSpeed; + + /** + * Construct a telemetry object, with the specified max speed of the robot + * + * @param maxSpeed Maximum speed in meters per second + */ + public Telemetry(double maxSpeed) { + MaxSpeed = maxSpeed; + SignalLogger.start(); + } + + /* What to publish over networktables for telemetry */ + private final NetworkTableInstance inst = NetworkTableInstance.getDefault(); + + /* Robot swerve drive state */ + private final NetworkTable driveStateTable = inst.getTable("DriveState"); + private final StructPublisher drivePose = driveStateTable.getStructTopic("Pose", Pose2d.struct).publish(); + private final StructPublisher driveSpeeds = driveStateTable.getStructTopic("Speeds", ChassisSpeeds.struct).publish(); + private final StructArrayPublisher driveModuleStates = driveStateTable.getStructArrayTopic("ModuleStates", SwerveModuleState.struct).publish(); + private final StructArrayPublisher driveModuleTargets = driveStateTable.getStructArrayTopic("ModuleTargets", SwerveModuleState.struct).publish(); + private final StructArrayPublisher driveModulePositions = driveStateTable.getStructArrayTopic("ModulePositions", SwerveModulePosition.struct).publish(); + private final DoublePublisher driveTimestamp = driveStateTable.getDoubleTopic("Timestamp").publish(); + private final DoublePublisher driveOdometryFrequency = driveStateTable.getDoubleTopic("OdometryFrequency").publish(); + + /* Robot pose for field positioning */ + private final NetworkTable table = inst.getTable("Pose"); + private final DoubleArrayPublisher fieldPub = table.getDoubleArrayTopic("robotPose").publish(); + private final StringPublisher fieldTypePub = table.getStringTopic(".type").publish(); + + /* Mechanisms to represent the swerve module states */ + private final Mechanism2d[] m_moduleMechanisms = new Mechanism2d[] { + new Mechanism2d(1, 1), + new Mechanism2d(1, 1), + new Mechanism2d(1, 1), + new Mechanism2d(1, 1), + }; + /* A direction and length changing ligament for speed representation */ + private final MechanismLigament2d[] m_moduleSpeeds = new MechanismLigament2d[] { + m_moduleMechanisms[0].getRoot("RootSpeed", 0.5, 0.5).append(new MechanismLigament2d("Speed", 0.5, 0)), + m_moduleMechanisms[1].getRoot("RootSpeed", 0.5, 0.5).append(new MechanismLigament2d("Speed", 0.5, 0)), + m_moduleMechanisms[2].getRoot("RootSpeed", 0.5, 0.5).append(new MechanismLigament2d("Speed", 0.5, 0)), + m_moduleMechanisms[3].getRoot("RootSpeed", 0.5, 0.5).append(new MechanismLigament2d("Speed", 0.5, 0)), + }; + /* A direction changing and length constant ligament for module direction */ + private final MechanismLigament2d[] m_moduleDirections = new MechanismLigament2d[] { + m_moduleMechanisms[0].getRoot("RootDirection", 0.5, 0.5) + .append(new MechanismLigament2d("Direction", 0.1, 0, 0, new Color8Bit(Color.kWhite))), + m_moduleMechanisms[1].getRoot("RootDirection", 0.5, 0.5) + .append(new MechanismLigament2d("Direction", 0.1, 0, 0, new Color8Bit(Color.kWhite))), + m_moduleMechanisms[2].getRoot("RootDirection", 0.5, 0.5) + .append(new MechanismLigament2d("Direction", 0.1, 0, 0, new Color8Bit(Color.kWhite))), + m_moduleMechanisms[3].getRoot("RootDirection", 0.5, 0.5) + .append(new MechanismLigament2d("Direction", 0.1, 0, 0, new Color8Bit(Color.kWhite))), + }; + + private final double[] m_poseArray = new double[3]; + private final double[] m_moduleStatesArray = new double[8]; + private final double[] m_moduleTargetsArray = new double[8]; + + /** Accept the swerve drive state and telemeterize it to SmartDashboard and SignalLogger. */ + //Secret OwO comment #1 + public void telemeterize(SwerveDriveState state) { + /* Telemeterize the swerve drive state */ + drivePose.set(state.Pose); + driveSpeeds.set(state.Speeds); + driveModuleStates.set(state.ModuleStates); + driveModuleTargets.set(state.ModuleTargets); + driveModulePositions.set(state.ModulePositions); + driveTimestamp.set(state.Timestamp); + driveOdometryFrequency.set(1.0 / state.OdometryPeriod); + + /* Also write to log file */ + m_poseArray[0] = state.Pose.getX(); + m_poseArray[1] = state.Pose.getY(); + m_poseArray[2] = state.Pose.getRotation().getDegrees(); + for (int i = 0; i < 4; ++i) { + m_moduleStatesArray[i*2 + 0] = state.ModuleStates[i].angle.getRadians(); + m_moduleStatesArray[i*2 + 1] = state.ModuleStates[i].speedMetersPerSecond; + m_moduleTargetsArray[i*2 + 0] = state.ModuleTargets[i].angle.getRadians(); + m_moduleTargetsArray[i*2 + 1] = state.ModuleTargets[i].speedMetersPerSecond; + } + + SignalLogger.writeDoubleArray("DriveState/Pose", m_poseArray); + SignalLogger.writeDoubleArray("DriveState/ModuleStates", m_moduleStatesArray); + SignalLogger.writeDoubleArray("DriveState/ModuleTargets", m_moduleTargetsArray); + SignalLogger.writeDouble("DriveState/OdometryPeriod", state.OdometryPeriod, "seconds"); + + /* Telemeterize the pose to a Field2d */ + fieldTypePub.set("Field2d"); + fieldPub.set(m_poseArray); + + /* Telemeterize the module states to a Mechanism2d */ + for (int i = 0; i < 4; ++i) { + m_moduleSpeeds[i].setAngle(state.ModuleStates[i].angle); + m_moduleDirections[i].setAngle(state.ModuleStates[i].angle); + m_moduleSpeeds[i].setLength(state.ModuleStates[i].speedMetersPerSecond / (2 * MaxSpeed)); + + SmartDashboard.putData("Module " + i, m_moduleMechanisms[i]); + } + } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Utility.java b/2024-2025/main-bot/src/main/java/frc/robot/Utility.java new file mode 100644 index 0000000..99169cf --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Utility.java @@ -0,0 +1,135 @@ +package frc.robot; + +import edu.wpi.first.wpilibj.DriverStation; + +/** + * The {@code Utility} class is used to store general methods that are used + * throughout the code. + *

+ * Current methods include: + *

    + *
  • {@link Utility#printLn}
  • + *
  • {@link Utility#clamp}
  • + *
  • {@link Utility#snapToEdge}
  • + *
+ * Additional methods may be added in the future if and when they are needed. + *

+ */ +public final class Utility { + + // Constructor // + // This is set to private as this is a utility class and + // should not be instantiated + // private Utility() { + // throw new UnsupportedOperationException( + // "This is a utility class and cannot be instantiated"); + // } + + /** + * The {@code printCounter} variable is used to count how many times the + * {@code System.out.println} method has been called. + * This is used in the {@link Utility#printLn} method. + */ + private static int printCounter = 0; + + /** + * The {@code printLn} method is used to print a message to the + * console every 50 times it is called. + * This is to slow down the messages as it overwhlems the roboRIO + * and causes the console to lag. + * + * @param messageString String to print to the console + * + * @see Utility#printCounter + */ + public static void printLn(final String messageString) {//i dont have any idea what this does but it's here so yay + if (printCounter == 50) { + System.out.println(messageString); + printCounter = 0; + } else { + printCounter++; + } + } + + /** + * The {@code log} method is similar to the printLn method. + * Instead of just printing a message, it will send it through DriverStation. + * THe values of the log type goes as follows: + * - 0 -> Info + * - 1 -> Warn + * - 3 -> Error + * @param messageString + * @param logType + */ + public static void log(final String messageString, final int logType) { + if (!(printCounter == 50)) { + printCounter++; + } + + if (logType == 1) { + System.out.println(messageString); + } else if (logType == 2) { + DriverStation.reportWarning(messageString, null); + } else if (logType == 3) { + DriverStation.reportError(messageString, null); + } + } + + + + + + + + + + + + + + + + + + + // BELOW ARE THE METHODS THAT ARE NOT USED IN THE CODE (IMPORTS FROM LAST YEAR) + + // /** + // * The {@code clamp} method is used to clamp a value between a minimum and + // * maximum value. + // *

+ // * If the value is less than the minimum, the minimum is returned. + // * If the value is greater than the maximum, the maximum is returned. + // * If the value is between the minimum and maximum, the value is returned. + // *

+ // * + // * @param value Value to clamp + // * @param min Minimum value + // * @param max Maximum value + // * + // * @return The clamped value + // */ + // public static double clamp(final double value, final double min, + // final double max) { + // return value < min ? min : value > max ? max : value; + // } + + // /** + // * Snap a value to the minimum or maximum value only if it is within a + // * threshold. + // * Although similar to the {@link Utility#clamp} method, + // * this method is used to snap a value to the minimum or maximum value + // * if it iswithin a threshold + // * + // * @param value Value to snap + // * @param min Minimum value + // * @param max Maximum value + // * @param threshold Threshold to snap to + // * + // * @return The snapped value + // */ + // public static double snapToEdge(final double value, final double min, + // final double max, final double threshold) { + // return value < -threshold ? min : value > threshold ? max : value; + // } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java b/2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java new file mode 100644 index 0000000..107aad7 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java b/2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java new file mode 100644 index 0000000..7481d3c --- /dev/null +++ b/2024-2025/main-bot/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"}) + 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/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java b/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java new file mode 100644 index 0000000..eb5d481 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java @@ -0,0 +1,286 @@ +package frc.robot.generated; + +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.CANBus; +import com.ctre.phoenix6.configs.*; +import com.ctre.phoenix6.hardware.*; +import com.ctre.phoenix6.signals.*; +import com.ctre.phoenix6.swerve.*; +import com.ctre.phoenix6.swerve.SwerveModuleConstants.*; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.numbers.N1; +import edu.wpi.first.math.numbers.N3; +import edu.wpi.first.units.measure.*; + +import frc.robot.subsystems.CommandSwerveDrivetrain; + +// Generated by the Tuner X Swerve Project Generator +// https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html +public class TunerConstants { + // Both sets of gains need to be tuned to your individual robot. + + // The steer motor uses any SwerveModule.SteerRequestType control request with the + // output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput + private static final Slot0Configs steerGains = new Slot0Configs() + .withKP(100).withKI(0).withKD(0.5) + .withKS(0.1).withKV(1.66).withKA(0) + .withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign); + // When using closed-loop control, the drive motor uses the control + // output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput + private static final Slot0Configs driveGains = new Slot0Configs() + .withKP(0.1).withKI(0).withKD(0) + .withKS(0).withKV(0.124); + + // The closed-loop output type to use for the steer motors; + // This affects the PID/FF gains for the steer motors + private static final ClosedLoopOutputType kSteerClosedLoopOutput = ClosedLoopOutputType.Voltage; + // The closed-loop output type to use for the drive motors; + // This affects the PID/FF gains for the drive motors + private static final ClosedLoopOutputType kDriveClosedLoopOutput = ClosedLoopOutputType.Voltage; + + // The type of motor used for the drive motor + private static final DriveMotorArrangement kDriveMotorType = DriveMotorArrangement.TalonFX_Integrated; + // The type of motor used for the drive motor + private static final SteerMotorArrangement kSteerMotorType = SteerMotorArrangement.TalonFX_Integrated; + + // The remote sensor feedback type to use for the steer motors; + // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote* + private static final SteerFeedbackType kSteerFeedbackType = SteerFeedbackType.FusedCANcoder; + + // The stator current at which the wheels start to slip; + // This needs to be tuned to your individual robot + private static final Current kSlipCurrent = Amps.of(120.0); + + // Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null. + // Some configs will be overwritten; check the `with*InitialConfigs()` API documentation. + private static final TalonFXConfiguration driveInitialConfigs = new TalonFXConfiguration(); + private static final TalonFXConfiguration steerInitialConfigs = new TalonFXConfiguration() + .withCurrentLimits( + new CurrentLimitsConfigs() + // Swerve azimuth does not require much torque output, so we can set a relatively low + // stator current limit to help avoid brownouts without impacting performance. + .withStatorCurrentLimit(Amps.of(60)) + .withStatorCurrentLimitEnable(true) + ); + private static final CANcoderConfiguration encoderInitialConfigs = new CANcoderConfiguration(); + // Configs for the Pigeon 2; leave this null to skip applying Pigeon 2 configs + private static final Pigeon2Configuration pigeonConfigs = null; + + // CAN bus that the devices are located on; + // All swerve devices must share the same CAN bus + public static final CANBus kCANBus = new CANBus("", "./logs/example.hoot"); + + // Theoretical free speed (m/s) at 12 V applied output; + // This needs to be tuned to your individual robot + public static final LinearVelocity kSpeedAt12Volts = MetersPerSecond.of(6.95); + + // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns; + // This may need to be tuned to your individual robot + private static final double kCoupleRatio = 3.5; + + private static final double kDriveGearRatio = 4.59375; + private static final double kSteerGearRatio = 13.371428571428572; + private static final Distance kWheelRadius = Inches.of(2); + + private static final boolean kInvertLeftSide = false; + private static final boolean kInvertRightSide = true; + + private static final int kPigeonId = 20; + + // These are only used for simulation + private static final MomentOfInertia kSteerInertia = KilogramSquareMeters.of(0.01); + private static final MomentOfInertia kDriveInertia = KilogramSquareMeters.of(0.01); + // Simulated voltage necessary to overcome friction + private static final Voltage kSteerFrictionVoltage = Volts.of(0.2); + private static final Voltage kDriveFrictionVoltage = Volts.of(0.2); + + public static final SwerveDrivetrainConstants DrivetrainConstants = new SwerveDrivetrainConstants() + .withCANBusName(kCANBus.getName()) + .withPigeon2Id(kPigeonId) + .withPigeon2Configs(pigeonConfigs); + + private static final SwerveModuleConstantsFactory ConstantCreator = + new SwerveModuleConstantsFactory() + .withDriveMotorGearRatio(kDriveGearRatio) + .withSteerMotorGearRatio(kSteerGearRatio) + .withCouplingGearRatio(kCoupleRatio) + .withWheelRadius(kWheelRadius) + .withSteerMotorGains(steerGains) + .withDriveMotorGains(driveGains) + .withSteerMotorClosedLoopOutput(kSteerClosedLoopOutput) + .withDriveMotorClosedLoopOutput(kDriveClosedLoopOutput) + .withSlipCurrent(kSlipCurrent) + .withSpeedAt12Volts(kSpeedAt12Volts) + .withDriveMotorType(kDriveMotorType) + .withSteerMotorType(kSteerMotorType) + .withFeedbackSource(kSteerFeedbackType) + .withDriveMotorInitialConfigs(driveInitialConfigs) + .withSteerMotorInitialConfigs(steerInitialConfigs) + .withEncoderInitialConfigs(encoderInitialConfigs) + .withSteerInertia(kSteerInertia) + .withDriveInertia(kDriveInertia) + .withSteerFrictionVoltage(kSteerFrictionVoltage) + .withDriveFrictionVoltage(kDriveFrictionVoltage); + + + // Front Left + private static final int kFrontLeftDriveMotorId = 42; + private static final int kFrontLeftSteerMotorId = 1; + private static final int kFrontLeftEncoderId = 0; + private static final Angle kFrontLeftEncoderOffset = Rotations.of(-0.432373046875); + private static final boolean kFrontLeftSteerMotorInverted = true; + private static final boolean kFrontLeftEncoderInverted = false; + + private static final Distance kFrontLeftXPos = Inches.of(11.25); + private static final Distance kFrontLeftYPos = Inches.of(11.25); + + // Front Right + private static final int kFrontRightDriveMotorId = 2; + private static final int kFrontRightSteerMotorId = 3; + private static final int kFrontRightEncoderId = 2; + private static final Angle kFrontRightEncoderOffset = Rotations.of(-0.282958984375); + private static final boolean kFrontRightSteerMotorInverted = true; + private static final boolean kFrontRightEncoderInverted = false; + + private static final Distance kFrontRightXPos = Inches.of(11.25); + private static final Distance kFrontRightYPos = Inches.of(-11.25); + + // Back Left + private static final int kBackLeftDriveMotorId = 6; + private static final int kBackLeftSteerMotorId = 7; + private static final int kBackLeftEncoderId = 6; + private static final Angle kBackLeftEncoderOffset = Rotations.of(-0.09326171875); + private static final boolean kBackLeftSteerMotorInverted = true; + private static final boolean kBackLeftEncoderInverted = false; + + private static final Distance kBackLeftXPos = Inches.of(-11.25); + private static final Distance kBackLeftYPos = Inches.of(11.25); + + // Back Right + private static final int kBackRightDriveMotorId = 4; + private static final int kBackRightSteerMotorId = 0; + private static final int kBackRightEncoderId = 4; + private static final Angle kBackRightEncoderOffset = Rotations.of(-0.111328125); + private static final boolean kBackRightSteerMotorInverted = true; + private static final boolean kBackRightEncoderInverted = false; + + private static final Distance kBackRightXPos = Inches.of(-11.25); + private static final Distance kBackRightYPos = Inches.of(-11.25); + + + public static final SwerveModuleConstants FrontLeft = + ConstantCreator.createModuleConstants( + kFrontLeftSteerMotorId, kFrontLeftDriveMotorId, kFrontLeftEncoderId, kFrontLeftEncoderOffset, + kFrontLeftXPos, kFrontLeftYPos, kInvertLeftSide, kFrontLeftSteerMotorInverted, kFrontLeftEncoderInverted + ); + public static final SwerveModuleConstants FrontRight = + ConstantCreator.createModuleConstants( + kFrontRightSteerMotorId, kFrontRightDriveMotorId, kFrontRightEncoderId, kFrontRightEncoderOffset, + kFrontRightXPos, kFrontRightYPos, kInvertRightSide, kFrontRightSteerMotorInverted, kFrontRightEncoderInverted + ); + public static final SwerveModuleConstants BackLeft = + ConstantCreator.createModuleConstants( + kBackLeftSteerMotorId, kBackLeftDriveMotorId, kBackLeftEncoderId, kBackLeftEncoderOffset, + kBackLeftXPos, kBackLeftYPos, kInvertLeftSide, kBackLeftSteerMotorInverted, kBackLeftEncoderInverted + ); + public static final SwerveModuleConstants BackRight = + ConstantCreator.createModuleConstants( + kBackRightSteerMotorId, kBackRightDriveMotorId, kBackRightEncoderId, kBackRightEncoderOffset, + kBackRightXPos, kBackRightYPos, kInvertRightSide, kBackRightSteerMotorInverted, kBackRightEncoderInverted + ); + + /** + * Creates a CommandSwerveDrivetrain instance. + * This should only be called once in your robot program,. + */ + public static CommandSwerveDrivetrain createDrivetrain() { + return new CommandSwerveDrivetrain( + DrivetrainConstants, FrontLeft, FrontRight, BackLeft, BackRight + ); + } + + + /** + * Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected device types. + */ + public static class TunerSwerveDrivetrain extends SwerveDrivetrain { + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + SwerveModuleConstants... modules + ) { + super( + TalonFX::new, TalonFX::new, CANcoder::new, + drivetrainConstants, modules + ); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz on + * CAN FD, and 100 Hz on CAN 2.0. + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + SwerveModuleConstants... modules + ) { + super( + TalonFX::new, TalonFX::new, CANcoder::new, + drivetrainConstants, odometryUpdateFrequency, modules + ); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz on + * CAN FD, and 100 Hz on CAN 2.0. + * @param odometryStandardDeviation The standard deviation for odometry calculation + * in the form [x, y, theta]ᵀ, with units in meters + * and radians + * @param visionStandardDeviation The standard deviation for vision calculation + * in the form [x, y, theta]ᵀ, with units in meters + * and radians + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + Matrix odometryStandardDeviation, + Matrix visionStandardDeviation, + SwerveModuleConstants... modules + ) { + super( + TalonFX::new, TalonFX::new, CANcoder::new, + drivetrainConstants, odometryUpdateFrequency, + odometryStandardDeviation, visionStandardDeviation, modules + ); + } + } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java new file mode 100644 index 0000000..cd75d4c --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java @@ -0,0 +1,68 @@ +package frc.robot.subsystems; + +//import edu.wpi.first.wpilibj.DigitalInput; +//import edu.wpi.first.wpilibj.Joystick; +//import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; + +import com.ctre.phoenix.motorcontrol.ControlMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +//import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +import edu.wpi.first.wpilibj2.command.Command; +//import edu.wpi.first.wpilibj2.command.Commands; + +public class AlgaeArmSubsystem extends SubsystemBase { + + //Motors + private final TalonSRX m_algaeBarMove; + + //Initialized Stuff + public AlgaeArmSubsystem(int algaeBarMoveID) { + m_algaeBarMove = new TalonSRX(algaeBarMoveID); + } + public Command commandMakeBarSpin(boolean APressed, boolean BPressed, boolean YPressed) {//makes, well, the bar spin + return runOnce( + () -> { + if (APressed) { + setAlgaeArmVoltage(0.5);//מציין מיקום man it sure is nice to hide secret messages in hebrew hungarian and albanian in that specific order + } + else if(BPressed) { + setAlgaeArmVoltage(-0.25);//helyőrző + } + else if (YPressed){ + setAlgaeArmVoltage(0.25);//vendmbajtes + } + else { + setAlgaeArmVoltage(0); + } + } + ); + } + public void setAlgaeArmVoltage(double voltage) {//place to move da algae bar + m_algaeBarMove.set(ControlMode.PercentOutput, voltage);//moves da algae bar + } + public Command PrintRand() { + return runOnce( + () -> { + System.out.println(Math.random() * 100); + }//a curly brace + );//a parenthesis followed by a semicolon + }//a curly brace + + //who placed these here? They lowkey clash wittewawy -_- + /*public Command commandAlgaeIntake(AlgaeArmSubsystem algaeArmSubsystem) { + return Commands.startEnd( + () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0.3), //placeholder + () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0.1), // placeholder + algaeArmSubsystem); + } + + public Command commandAlgaeOuttake(AlgaeArmSubsystem algaeArmSubsystem) { + return Commands.startEnd( + () -> m_algaeBarMove.set(ControlMode.PercentOutput, -0.3), //placeholder + () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0), //placeholder + algaeArmSubsystem); + }*/ + + +}//a curly brace \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java new file mode 100644 index 0000000..3f70c28 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java @@ -0,0 +1,327 @@ +package frc.robot.subsystems; + +import static edu.wpi.first.units.Units.*; + +import java.util.function.Supplier; + +import com.ctre.phoenix6.SignalLogger; +import com.ctre.phoenix6.Utils; +import com.ctre.phoenix6.swerve.SwerveDrivetrainConstants; +import com.ctre.phoenix6.swerve.SwerveModuleConstants; +import com.ctre.phoenix6.swerve.SwerveRequest; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.config.PIDConstants; +import com.pathplanner.lib.config.RobotConfig; +import com.pathplanner.lib.controllers.PPHolonomicDriveController; + +import edu.wpi.first.math.Matrix; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.math.numbers.N1; +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.Notifier; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Subsystem; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine; + +import frc.robot.generated.TunerConstants.TunerSwerveDrivetrain; + +/** + * Class that extends the Phoenix 6 SwerveDrivetrain class and implements + * Subsystem so it can easily be used in command-based projects. + */ +public class CommandSwerveDrivetrain extends TunerSwerveDrivetrain implements Subsystem { + private static final double kSimLoopPeriod = 0.005; // 5 ms + private Notifier m_simNotifier = null; + private double m_lastSimTime; + + /* Blue alliance sees forward as 0 degrees (toward red alliance wall) */ + private static final Rotation2d kBlueAlliancePerspectiveRotation = Rotation2d.kZero; + /* Red alliance sees forward as 180 degrees (toward blue alliance wall) */ + private static final Rotation2d kRedAlliancePerspectiveRotation = Rotation2d.k180deg; + /* Keep track if we've ever applied the operator perspective before or not */ + private boolean m_hasAppliedOperatorPerspective = false; + + /** Swerve request to apply during robot-centric path following */ + private final SwerveRequest.ApplyRobotSpeeds m_pathApplyRobotSpeeds = new SwerveRequest.ApplyRobotSpeeds(); + + /* Swerve requests to apply during SysId characterization */ + private final SwerveRequest.SysIdSwerveTranslation m_translationCharacterization = new SwerveRequest.SysIdSwerveTranslation(); + private final SwerveRequest.SysIdSwerveSteerGains m_steerCharacterization = new SwerveRequest.SysIdSwerveSteerGains(); + private final SwerveRequest.SysIdSwerveRotation m_rotationCharacterization = new SwerveRequest.SysIdSwerveRotation(); + + /* SysId routine for characterizing translation. This is used to find PID gains for the drive motors. */ + private final SysIdRoutine m_sysIdRoutineTranslation = new SysIdRoutine( + new SysIdRoutine.Config( + null, // Use default ramp rate (1 V/s) + Volts.of(4), // Reduce dynamic step voltage to 4 V to prevent brownout + null, // Use default timeout (10 s) + // Log state with SignalLogger class + state -> SignalLogger.writeString("SysIdTranslation_State", state.toString()) + ), + new SysIdRoutine.Mechanism( + output -> setControl(m_translationCharacterization.withVolts(output)), + null, + this + ) + ); + + /* SysId routine for characterizing steer. This is used to find PID gains for the steer motors. */ + private final SysIdRoutine m_sysIdRoutineSteer = new SysIdRoutine( + new SysIdRoutine.Config( + null, // Use default ramp rate (1 V/s) + Volts.of(7), // Use dynamic voltage of 7 V + null, // Use default timeout (10 s) + // Log state with SignalLogger class + state -> SignalLogger.writeString("SysIdSteer_State", state.toString()) + ), + new SysIdRoutine.Mechanism( + volts -> setControl(m_steerCharacterization.withVolts(volts)), + null, + this + ) + ); + + /* + * SysId routine for characterizing rotation. + * This is used to find PID gains for the FieldCentricFacingAngle HeadingController. + * See the documentation of SwerveRequest.SysIdSwerveRotation for info on importing the log to SysId. + */ + private final SysIdRoutine m_sysIdRoutineRotation = new SysIdRoutine( + new SysIdRoutine.Config( + /* This is in radians per second², but SysId only supports "volts per second" */ + Volts.of(Math.PI / 6).per(Second), + /* This is in radians per second, but SysId only supports "volts" */ + Volts.of(Math.PI), + null, // Use default timeout (10 s) + // Log state with SignalLogger class + state -> SignalLogger.writeString("SysIdRotation_State", state.toString()) + ), + new SysIdRoutine.Mechanism( + output -> { + /* output is actually radians per second, but SysId only supports "volts" */ + setControl(m_rotationCharacterization.withRotationalRate(output.in(Volts))); + /* also log the requested output for SysId */ + SignalLogger.writeDouble("Rotational_Rate", output.in(Volts)); + }, + null, + this + ) + ); + + /* The SysId routine to test */ + private SysIdRoutine m_sysIdRoutineToApply = m_sysIdRoutineTranslation; + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param modules Constants for each specific module + */ + public CommandSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + SwerveModuleConstants... modules + ) { + super(drivetrainConstants, modules); + if (Utils.isSimulation()) { + startSimThread(); + } + configureAutoBuilder(); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz on + * CAN FD, and 100 Hz on CAN 2.0. + * @param modules Constants for each specific module + */ + public CommandSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + SwerveModuleConstants... modules + ) { + super(drivetrainConstants, odometryUpdateFrequency, modules); + if (Utils.isSimulation()) { + startSimThread(); + } + configureAutoBuilder(); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not construct + * the devices themselves. If they need the devices, they can access them through + * getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz on + * CAN FD, and 100 Hz on CAN 2.0. + * @param odometryStandardDeviation The standard deviation for odometry calculation + * in the form [x, y, theta]ᵀ, with units in meters + * and radians + * @param visionStandardDeviation The standard deviation for vision calculation + * in the form [x, y, theta]ᵀ, with units in meters + * and radians + * @param modules Constants for each specific module + */ + public CommandSwerveDrivetrain( + SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, + Matrix odometryStandardDeviation, + Matrix visionStandardDeviation, + SwerveModuleConstants... modules + ) { + super(drivetrainConstants, odometryUpdateFrequency, odometryStandardDeviation, visionStandardDeviation, modules); + if (Utils.isSimulation()) { + startSimThread(); + } + configureAutoBuilder(); + } + + private void configureAutoBuilder() { + try { + var config = RobotConfig.fromGUISettings(); + AutoBuilder.configure( + () -> getState().Pose, // Supplier of current robot pose + this::resetPose, // Consumer for seeding pose against auto + () -> getState().Speeds, // Supplier of current robot speeds + // Consumer of ChassisSpeeds and feedforwards to drive the robot + (speeds, feedforwards) -> setControl( + m_pathApplyRobotSpeeds.withSpeeds(speeds) + .withWheelForceFeedforwardsX(feedforwards.robotRelativeForcesXNewtons()) + .withWheelForceFeedforwardsY(feedforwards.robotRelativeForcesYNewtons()) + ), + new PPHolonomicDriveController( + // PID constants for translation + new PIDConstants(10, 0, 0), + // PID constants for rotation + new PIDConstants(7, 0, 0) + ), + config, + // Assume the path needs to be flipped for Red vs Blue, this is normally the case + () -> DriverStation.getAlliance().orElse(Alliance.Blue) == Alliance.Red, + this // Subsystem for requirements + ); + } catch (Exception ex) { + DriverStation.reportError("Failed to load PathPlanner config and configure AutoBuilder", ex.getStackTrace()); + } + } + + /** + * Returns a command that applies the specified control request to this swerve drivetrain. + * + * @param request Function returning the request to apply + * @return Command to run + */ + public Command applyRequest(Supplier requestSupplier) { + return run(() -> this.setControl(requestSupplier.get())); + } + + /** + * Runs the SysId Quasistatic test in the given direction for the routine + * specified by {@link #m_sysIdRoutineToApply}. + * + * @param direction Direction of the SysId Quasistatic test + * @return Command to run + */ + public Command sysIdQuasistatic(SysIdRoutine.Direction direction) { + return m_sysIdRoutineToApply.quasistatic(direction); + } + + /** + * Runs the SysId Dynamic test in the given direction for the routine + * specified by {@link #m_sysIdRoutineToApply}. + * + * @param direction Direction of the SysId Dynamic test + * @return Command to run + */ + public Command sysIdDynamic(SysIdRoutine.Direction direction) { + return m_sysIdRoutineToApply.dynamic(direction); + } + + @Override + public void periodic() { + /* + * Periodically try to apply the operator perspective. + * If we haven't applied the operator perspective before, then we should apply it regardless of DS state. + * This allows us to correct the perspective in case the robot code restarts mid-match. + * Otherwise, only check and apply the operator perspective if the DS is disabled. + * This ensures driving behavior doesn't change until an explicit disable event occurs during testing. + */ + if (!m_hasAppliedOperatorPerspective || DriverStation.isDisabled()) { + DriverStation.getAlliance().ifPresent(allianceColor -> { + setOperatorPerspectiveForward( + allianceColor == Alliance.Red + ? kRedAlliancePerspectiveRotation + : kBlueAlliancePerspectiveRotation + ); + m_hasAppliedOperatorPerspective = true; + }); + } + } + + private void startSimThread() { + m_lastSimTime = Utils.getCurrentTimeSeconds(); + + /* Run simulation at a faster rate so PID gains behave more reasonably */ + m_simNotifier = new Notifier(() -> { + final double currentTime = Utils.getCurrentTimeSeconds(); + double deltaTime = currentTime - m_lastSimTime; + m_lastSimTime = currentTime; + + /* use the measured time delta, get battery voltage from WPILib */ + updateSimState(deltaTime, RobotController.getBatteryVoltage()); + }); + m_simNotifier.startPeriodic(kSimLoopPeriod); + } + + /** + * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate + * while still accounting for measurement noise. + * + * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera. + * @param timestampSeconds The timestamp of the vision measurement in seconds. + */ + @Override + public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) { + super.addVisionMeasurement(visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds)); + } + + /** + * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate + * while still accounting for measurement noise. + *

+ * Note that the vision measurement standard deviations passed into this method + * will continue to apply to future measurements until a subsequent call to + * {@link #setVisionMeasurementStdDevs(Matrix)} or this method. + * + * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera. + * @param timestampSeconds The timestamp of the vision measurement in seconds. + * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement + * in the form [x, y, theta]ᵀ, with units in meters and radians. + */ + @Override + public void addVisionMeasurement( + Pose2d visionRobotPoseMeters, + double timestampSeconds, + Matrix visionMeasurementStdDevs + ) { + super.addVisionMeasurement(visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds), visionMeasurementStdDevs); + } +} diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java new file mode 100644 index 0000000..efb215e --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java @@ -0,0 +1,108 @@ +package frc.robot.subsystems; +//"A good programmers code needs not any comments, for it comments on itself" - IDK who I made it up +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.Constants.CoralArmConstants; + +import java.util.function.BooleanSupplier; + +import com.ctre.phoenix.motorcontrol.ControlMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj.Encoder; + +public class CoralArmSubsystem extends SubsystemBase{ + //Motor(s) + private final TalonSRX m_CoralArmMotor; + //Encoder + private final Encoder m_coralEncoder; + // Current state + private CoralArmLevels currentCoralArmState; + + public CoralArmSubsystem(int coralArmID) { + //Initialized Stuff + m_coralEncoder = new Encoder(2,3); + m_CoralArmMotor = new TalonSRX(coralArmID); + currentCoralArmState = CoralArmLevels.Down; + } + + public enum CoralArmLevels { + Down, + Up, + Stop + } + + public Command CommandsetCoralArmVoltage(double voltage, CoralArmLevels desiredState) { + return runOnce( + () -> { + if (voltage > 0) { + if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue) { + setCoralArmVoltage(voltage); + } + else if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopBuffer) { + System.out.println("How is the arm not broken yet? Amazingly, you didn't break it."); + setCoralArmVoltage(0); + } + else { + setCoralArmVoltage(0); + currentCoralArmState = desiredState; + } + } + else if (voltage < 0) { + if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderBottomValue) { + setCoralArmVoltage(voltage); + } + else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderBottomBuffer) { + System.out.println("How is the arm not broken yet? Amazingly, you didn't break it."); + setCoralArmVoltage(0); + } + else { + setCoralArmVoltage(0); + currentCoralArmState = desiredState; + } + } + else { + setCoralArmVoltage(0); + currentCoralArmState = desiredState; + } + } + ); + } + public void setCoralArmVoltage(double voltage) { + m_CoralArmMotor.set(ControlMode.PercentOutput, voltage); + //Add more motors here if neccessary + } + + public CoralArmLevels getCurrentCoralArmState() { + return currentCoralArmState; + } + + public BooleanSupplier isCoralArmAtDesiredState(CoralArmLevels desiredState) { + return () -> currentCoralArmState == desiredState; + } + + public Command emergencyStop() {// Just in case the driver wants to stop the arm without stopping the whole program (and for some reason the Command Voltage function doesn't stop the arm after it gets above a certain point). + return runOnce ( + () -> { + setCoralArmVoltage(0); + System.out.println("Emergency Stop Pressed! Stay Still And Don't Move The Arm..."); + if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue) { + do {//Hey look at that, a do while loop has a use! + setCoralArmVoltage(-0.25); + } while (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue); + setCoralArmVoltage(0); + } + else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue) { + do { + setCoralArmVoltage(0.25); + } while (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue); + setCoralArmVoltage(0); + } + else { + System.out.println("Arm is already at top"); + } + System.out.println("Arm Is Now Reset. Carry On."); + } + ); + } +} + \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java new file mode 100644 index 0000000..785a867 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -0,0 +1,197 @@ +package frc.robot.subsystems; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import edu.wpi.first.wpilibj.DigitalInput; +import edu.wpi.first.wpilibj2.command.Command; +//import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj.Encoder; + +import java.util.function.BooleanSupplier; + +//import com.ctre.phoenix.Util; +import com.ctre.phoenix6.hardware.TalonFX; +import frc.robot.Utility; +import frc.robot.Constants.ElevatorConstants; + +public class ElevatorSubsystem extends SubsystemBase {//makes elevator subsystem into an actual subsystem + + // Motors + private final TalonFX m_elevatorLeftMotor; + private final TalonFX m_elevatorRightMotor; + // Limit Switches + private DigitalInput m_elevatorTopLimitSwitch; + private DigitalInput m_elevatorBottomLimitSwitch; + // Encoder + private final Encoder m_Encoder1; + // private TitanQuadEncoder m_elevatorEncoder; + private double m_elevatorEncoder; // Placeholder + + private ElevatorLevels currentElevatorState; + + + + + public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {//this is da place where stuff is actually initialized + m_elevatorEncoder = 0; // Placeholder + m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); + m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); + m_elevatorTopLimitSwitch = new DigitalInput(1); + m_elevatorBottomLimitSwitch = new DigitalInput(2); + m_Encoder1 = new Encoder(0, 1); + currentElevatorState = ElevatorLevels.Lowest; + } + +public enum ElevatorLevels { + Lowest, // bottom + Level1, // Levels of the reef + Level2, + Level3, + Level4, + Highest // top +} + +//The commands below that are commented are currently not being used and have been replaced. They are kept just cause + + //public Command raiseElevator() { + // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(0.1); + // }); + //} + + // public Command lowerElevator() { + // // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(-0.1); + // }); + // } + + + public boolean isElevatorAtTop() { + // Placeholder + if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + return false; + } + else if(m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()){ + System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + return true; + } + else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + return true; + } + else { + return false; + } + } + + public boolean isElevatorAtBottom() { + // Placeholder + if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + return false; + } + else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && m_elevatorBottomLimitSwitch.get()){ + System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + return true; + } + else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + return true; + } + else { + return false; + } + } + + public void setMotorElevatorSpeed(double voltage) { + // Placeholder + + // BEN: If this method truly will be run once (as above), then the stop conditions will only be evaluated once. + // We need to check the conditions continually somehow. + + if (voltage > 0 && isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up + m_elevatorLeftMotor.set(voltage); + m_elevatorRightMotor.set(-voltage); + Utility.printLn("Elevator is moving up"); + } else if (voltage < 0 && isElevatorAtBottom()) {//or if its at bottom make move down + m_elevatorLeftMotor.set(voltage); + m_elevatorRightMotor.set(-voltage); + Utility.printLn("Elevator is moving down"); + } else {//otherwise just no speed + m_elevatorLeftMotor.set(0); + m_elevatorRightMotor.set(0); + Utility.printLn("Elevator is at the top or bottom and therefore the motors have been stopped"); + } + + } + + public void moveElevatorToPreset(ElevatorLevels desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements + switch (desiredPreset) { + case Level1: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + break; + case Level2: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + break; + case Level3: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + break; + case Level4: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + break; + } + } + /** + * The moveElevatorToPosition method takes a double as a parameter. + * This value represents the number of rotations necessary to reach the target position. + * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a selective factorial. Yeah idk why I said that. Moving on + * @param position + */ + public void moveElevatorToPosition (double position, ElevatorLevels desiredPreset) {//moves elevator to a certain position based on the encoder value + // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! + if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom + m_elevatorLeftMotor.set(-ElevatorConstants.kMotorElevatorSpeed); + m_elevatorRightMotor.set(ElevatorConstants.kMotorElevatorSpeed); + Utility.printLn("Elevator is moving down"); + } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top + m_elevatorLeftMotor.set(ElevatorConstants.kMotorElevatorSpeed); + m_elevatorRightMotor.set(-ElevatorConstants.kMotorElevatorSpeed); + Utility.printLn("Elevator is moving up"); + } else { + m_elevatorLeftMotor.set(0); + m_elevatorRightMotor.set(0); + Utility.printLn("Elevator is at the top, bottom, desired position, or error occurred and therefore the motors have been stopped"); + currentElevatorState = desiredPreset; + } + } + + public ElevatorLevels getElevatorState() { + return currentElevatorState; + } + + public BooleanSupplier isElevatorAtDesiredState(ElevatorLevels desiredState) { + return () -> currentElevatorState == desiredState; + } + + public Command commandVoltage(double leftTrigger, double rightTrigger) {//just exists because just felt like it + return runOnce( + () -> { + setMotorElevatorSpeed(rightTrigger - leftTrigger); + } + ); + } + + public Command commandMoveToPreset(ElevatorLevels desiredPreset) {//The first function in a very tall dependancy tree + return runOnce( + () -> { + moveElevatorToPreset(desiredPreset); + } + ); + } + +} +//a comment because why not :) +// This is a very cool comment *wink* *wink* *nudge* *nudge* \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java new file mode 100644 index 0000000..6b375da --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/vendordeps/AdvantageKit.json b/2024-2025/main-bot/vendordeps/AdvantageKit.json new file mode 100644 index 0000000..03df051 --- /dev/null +++ b/2024-2025/main-bot/vendordeps/AdvantageKit.json @@ -0,0 +1,35 @@ +{ + "fileName": "AdvantageKit.json", + "name": "AdvantageKit", + "version": "4.1.0", + "uuid": "d820cc26-74e3-11ec-90d6-0242ac120003", + "frcYear": "2025", + "mavenUrls": [ + "https://frcmaven.wpi.edu/artifactory/littletonrobotics-mvn-release/" + ], + "jsonUrl": "https://github.com/Mechanical-Advantage/AdvantageKit/releases/latest/download/AdvantageKit.json", + "javaDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-java", + "version": "4.1.0" + } + ], + "jniDependencies": [ + { + "groupId": "org.littletonrobotics.akit", + "artifactId": "akit-wpilibio", + "version": "4.1.0", + "skipInvalidPlatforms": false, + "isJar": false, + "validPlatforms": [ + "linuxathena", + "linuxx86-64", + "linuxarm64", + "osxuniversal", + "windowsx86-64" + ] + } + ], + "cppDependencies": [] +} \ No newline at end of file diff --git a/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json b/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json new file mode 100644 index 0000000..71e25f3 --- /dev/null +++ b/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json @@ -0,0 +1,38 @@ +{ + "fileName": "PathplannerLib-2025.2.1.json", + "name": "PathplannerLib", + "version": "2025.2.1", + "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.1" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.pathplanner.lib", + "artifactId": "PathplannerLib-cpp", + "version": "2025.2.1", + "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/2024-2025/main-bot/vendordeps/Phoenix5-replay-5.35.1.json b/2024-2025/main-bot/vendordeps/Phoenix5-replay-5.35.1.json new file mode 100644 index 0000000..bdf2f44 --- /dev/null +++ b/2024-2025/main-bot/vendordeps/Phoenix5-replay-5.35.1.json @@ -0,0 +1,221 @@ +{ + "fileName": "Phoenix5-replay-5.35.1.json", + "name": "CTRE-Phoenix (v5)", + "version": "5.35.1", + "frcYear": "2025", + "uuid": "fbc886a4-2cec-40c0-9835-71086a8cc3df", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix/Phoenix5-replay-frc2025-latest.json", + "requires": [ + { + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "errorMessage": "Phoenix 5 requires low-level libraries from Phoenix 6. Please add the Phoenix 6 vendordep before adding Phoenix 5.", + "offlineFileName": "Phoenix6-replay-frc2025-latest.json", + "onlineUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-replay-frc2025-latest.json" + } + ], + "conflictsWith": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Users must use the regular Phoenix 5 vendordep when using the regular Phoenix 6 vendordep.", + "offlineFileName": "Phoenix6-frc2025-latest.json" + }, + { + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "errorMessage": "Users cannot have both the replay and regular Phoenix 5 vendordeps in their robot program.", + "offlineFileName": "Phoenix5-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.35.1" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.35.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "cci-replay", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.35.1", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "wpiapi-cpp-replay", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "api-cpp-replay", + "version": "5.35.1", + "libName": "CTRE_PhoenixReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.replay", + "artifactId": "cci-replay", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_Phoenix_WPISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "api-cpp-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixSim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix.sim", + "artifactId": "cci-sim", + "version": "5.35.1", + "libName": "CTRE_PhoenixCCISim", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/main-bot/vendordeps/Phoenix6-replay-25.2.1.json b/2024-2025/main-bot/vendordeps/Phoenix6-replay-25.2.1.json new file mode 100644 index 0000000..77acc5a --- /dev/null +++ b/2024-2025/main-bot/vendordeps/Phoenix6-replay-25.2.1.json @@ -0,0 +1,467 @@ +{ + "fileName": "Phoenix6-replay-25.2.1.json", + "name": "CTRE-Phoenix (v6) Replay", + "version": "25.2.1", + "frcYear": "2025", + "uuid": "e7900d8d-826f-4dca-a1ff-182f658e98af", + "mavenUrls": [ + "https://maven.ctr-electronics.com/release/" + ], + "jsonUrl": "https://maven.ctr-electronics.com/release/com/ctre/phoenix6/latest/Phoenix6-replay-frc2025-latest.json", + "conflictsWith": [ + { + "uuid": "e995de00-2c64-4df5-8831-c1441420ff19", + "errorMessage": "Users can not have both the replay and regular Phoenix 6 vendordeps in their robot program.", + "offlineFileName": "Phoenix6-frc2025-latest.json" + } + ], + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-java", + "version": "25.2.1" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "api-cpp", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "api-cpp-replay", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "tools-replay", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "api-cpp-sim", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "tools-sim", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simTalonSRX", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simVictorSPX", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simPigeonIMU", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simCANCoder", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFX", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProTalonFXS", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANcoder", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProPigeon2", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "simProCANrange", + "version": "25.2.1", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix6", + "artifactId": "wpiapi-cpp", + "version": "25.2.1", + "libName": "CTRE_Phoenix6_WPI", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6", + "artifactId": "tools", + "version": "25.2.1", + "libName": "CTRE_PhoenixTools", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "wpiapi-cpp-replay", + "version": "25.2.1", + "libName": "CTRE_Phoenix6_WPIReplay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.replay", + "artifactId": "tools-replay", + "version": "25.2.1", + "libName": "CTRE_PhoenixTools_Replay", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "hwsim" + }, + { + "groupId": "com.ctre.phoenix6.sim", + "artifactId": "wpiapi-cpp-sim", + "version": "25.2.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": "25.2.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": "25.2.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": "25.2.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": "25.2.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": "simCANCoder", + "version": "25.2.1", + "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.2.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": "25.2.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": "25.2.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": "25.2.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": "25.2.1", + "libName": "CTRE_SimProCANrange", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxx86-64", + "linuxarm64", + "osxuniversal" + ], + "simMode": "swsim" + } + ] +} \ No newline at end of file diff --git a/2024-2025/main-bot/vendordeps/Studica-2025.0.1.json b/2024-2025/main-bot/vendordeps/Studica-2025.0.1.json new file mode 100644 index 0000000..5010be0 --- /dev/null +++ b/2024-2025/main-bot/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/2024-2025/main-bot/vendordeps/WPILibNewCommands.json b/2024-2025/main-bot/vendordeps/WPILibNewCommands.json new file mode 100644 index 0000000..3718e0a --- /dev/null +++ b/2024-2025/main-bot/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" + ] + } + ] +} From fc3af9682cf82de0b4102edaea5db92bf575a2a3 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Thu, 13 Feb 2025 17:53:57 -0600 Subject: [PATCH 03/32] Ben: These files should not be checked in --- .../.gradle/7.5.1/checksums/md5-checksums.bin | Bin 36397 -> 0 bytes .../.gradle/7.5.1/checksums/sha1-checksums.bin | Bin 87557 -> 0 bytes .../executionHistory/executionHistory.bin | Bin 178787 -> 0 bytes .../.gradle/7.5.1/fileChanges/last-build.bin | Bin 1 -> 0 bytes .../.gradle/7.5.1/fileHashes/fileHashes.bin | Bin 70215 -> 0 bytes .../7.5.1/fileHashes/resourceHashesCache.bin | Bin 34545 -> 0 bytes .../.gradle/buildOutputCleanup/outputFiles.bin | Bin 24131 -> 0 bytes .../main-bot/gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes .../main-bot/gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 0 bytes .../gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../main-bot/gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle/wrapper/gradle-wrapper.jar | Bin 60756 -> 0 bytes 14 files changed, 0 insertions(+), 0 deletions(-) delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/checksums/md5-checksums.bin delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/checksums/sha1-checksums.bin delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.bin delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/fileChanges/last-build.bin delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/fileHashes/fileHashes.bin delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/fileHashes/resourceHashesCache.bin delete mode 100755 2022-2023/main-bot/.gradle/buildOutputCleanup/outputFiles.bin delete mode 100755 2022-2023/main-bot/gradle/wrapper/gradle-wrapper.jar delete mode 100644 2023-2024/main-bot/gradle/wrapper/gradle-wrapper.jar delete mode 100644 2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar delete mode 100644 2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar delete mode 100644 2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar delete mode 100644 2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar delete mode 100644 Example_Project/gradle/wrapper/gradle-wrapper.jar diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/md5-checksums.bin b/2022-2023/main-bot/.gradle/7.5.1/checksums/md5-checksums.bin deleted file mode 100755 index ffaa3ce6b2fa9befc716a54fde36ee8db42fd6d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36397 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 diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/sha1-checksums.bin b/2022-2023/main-bot/.gradle/7.5.1/checksums/sha1-checksums.bin deleted file mode 100755 index ee365d5e8c6e55f14e7441618254d7464af00930..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87557 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 Z8b|CLq`-&)X*{uWfLb;Iqwv_d{{t1kxt;(3 diff --git a/2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.bin b/2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.bin deleted file mode 100755 index b53bfdce313ecfc5e0cccc6b042577acb2337de8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178787 zcmeEP2Xqt1)7MERm=ZWTgkD1XWA#ceHqG=7Q6MMjWXnY|l58*#TIgV7z!(DtObI=d z&GJ-UCxJW(gcHjQ?P1moXh<_rwr1;6Fn*rkwxFji)om z`s5>vfzN;`Z*e}bW73YhJ;`$3$TCcMmx|@J!+IBdPnPpbE0}WT6O@mq>g4@DPXc)o z$df>x1o9-1CxJW(x1o9-1CxJW(ZmR%qJ8p;+4u~)b zjV7zjE)23_hA`Y}vIq@$M3^O7*bTD=;dXyq7a7#j6oz-hY@z-9alOTg*>M9}&aZTh zutAasp@$bg9(3R5XC#N)1HG7FV0a^YkQVi|-EFwlrnTc(xJ}!_V!*Xhu~eqD*-hpk zt2P`nnf-JYyH*!z3NvWQs#>c>XCZnNVzZce=$T3V7yJ<>YdLCX%kkrqDc=vK2-I zGyHn_+w4}*trixu9qC{2sPc#>Th&{RMh zK$UiQ6duN#!VutQy~P0V@e&**1MRrk2E^kJl!+J56dN&9SVwb!)oQUKF^)ouHI(lp z0pB_@l3n&$^evsF9Psnlt<&uZ@qxHL(vIoEa9&bozlTJH6EZL4_%Q^F!l*~y9_Ukx zFwAD-&0=agf9wGi5%iT8$NXf)!*I-o^Wtsbjnm_V3Sf4V&J+d|N${IQ5^pD8CQf<2 z33`GDAPz2_;neX?k-9LGUJG8%Fq4i_^KdK*H*3KwOO$HKSC;-gg!%L%0Fu-~iBRHF z>PNgPM5!cPBsXZVmeP<>3!<)1U)=_#W{N2dD`y#3z@xqZH!ZtX^!NK7rFP zzwf)o>O!jh%D?4yzs+_q3gKGn1LDjogcyfBto!SYluPCA#UGpU*U>c(JYcN1k&LMi zh%u`WVmvB-wRN(wPOZVq5{Fi;r9Q-e<#vztSU7G43TBPMty=aM7zB%s{0V~6Yk1G1 zbyB{+GHlqkGOL#Ck@7FO4J8;0(}I5xW2OY+*Lqge=`mA%1!+BbP19bqF5rR8fr6QF zJE3Rb1LDdmgcxt^8M$SD%=thh`PH>o8R2HnG(6I73L`iZpIp|CQiyk@%~!+TZmC!6 za6td~>plxh@b9}VWGupJ3bSKDTILUkyGt>|e$VNIwh#FYhX`wQyFII6w3h$UZT9wH zlhqJ`S?$qUqg78%vz7=j=tpU(4+@Q3s1%C*hO#0Bd?%=hW zYWx#fKmuj}EgljcM*h#wW(to8!?j|eT*5B!BMK-cTb6G))NA~SYaR3l@7i~*vGSi} zfeDzawMNWl$E^barw~jZ3RVbO^5v$#_!%%TahS{otyCzH3grxuquh@wr$ER*EFUrd zd4pLzwO^~>o0jjFrB^%)ATYDpVh1w`Sa!fTSUd-bgTJ5eSh%@0sdc~yfXOl(GaGF5M8+zC zUIy@jf@u=|9eOL41V(Q=v!m*xsQXP9`qn8C$Ul_@vAF7@5zNz88(SIa5|%d3n>eli z;s3?fI@0=bNWPHSDITc2(EwIbE?me07865*-pVN-y5(`)DTrh}#-Q zzGKd@j(okGBV9(7K2K`Jc3s8q;O~24lD%D>x7dk#%nZ=VyTUO4@KvAMQj zL`$DnW6j(5dErxgd`WG_jgz^vTtrWI{;9YeXUYO{*_eyq@I zL-X<*+?O`B6m)Nyh{j#eqzzg(@p(YIYmu*dzYFn5t36pHqSfMxtsb#_dzqp2%<~Q% zI(jZQmx$<|RW&C~xYDUnGru*DUpy_j^FLZ3qGNx4Tyo>s$buiMHjBAjWyiprUml`0 z*0;K|v(850;IPnl(fE3qP?dC}&Hcb^JhH-F%MKfb&m zM1L7L=T=(a|Fn0{)$Fvda-)~-_v1^;L9{u)YslJ1-TvM^w9?7AR)8bo5Cjy**alrnFx*hEWWf9MDO=i4D>C$#i!v<{H6~Z6j+wC3qiEfk1f7$uufc6 zbgFOf?S>2t?RmYd)n|0=`**5!8dke`my-@du5*vr$!gK#Hk-vtn6i1Z8M~#=z#+)n`XDT1xqB+DtUq zDG>mv$h}V-2z>F?4v3l z3^&ypb(nba)AJENtqMhfOR(Zr;N1F=`1fU(pbu};7hHm*`zTA!z?^Ey)eX4si&7+B z6pkB^bYb-a{yfGLcLntiTbdHvvx)@mohLP%ZvUD;PbyM&SAy5}_U#=erUfFuN%VN8(w(5Mux_=n6(m zW0#r;*CNiF%PxzI0A3&P7TLOjA=wVRw!TXsf0&VQF#2=o#p_m z(c|&$1G+WyYty0Sbl2bMz9+kua(d-%?_E(i`tKAm0sQk4aHZwQ!1=_bpM2s{;4!72 zxTF8hCl7w*Jb6%-?Y=voyp-r7{N%~PEIoNC@Owi~e#{_Kgi_8IOmsXi$q(B+oWzK= z0D=%R*_Rg)pO^=o%LxRT;F!o+YphSq-d}8vTAh%SrGPW1veyjGx%cQ72c$1^WzEq7 z6rQ7z1V;}=EJBYA6~IXDfv*_0{2{X_dwU3Jg?F*CdxXj}FEVTuBvEDVa(3UArxY}tiyTh0D#5t>d(rKFo3#D5gbl6IYB*Bh&mZLc< z!&MdlIyaYD_O8P=RAe3llhqgtY3|ur1so$QlE`3#{PbR8eHC!bugD^5ZSh%OzWcV# ztT^srBQ26Zz8SEG-~E^JOSW49$9PM>lvG8>OX+^MziiSKaBRACx8Yy__q+WiQ?Gzy z>!rI5MF_ah`AZgH0mlSPbB4kO+~@oy!?1v38KyZyu>`EW%qPcR*U*&D2M)jnV`Lhs z+b-b+Wjr-Ya;$cvga)9b9431 zS-k&83v|x<-2G|bOUv-Qpaf^=Y+g_T;SF`MqH$Km?yvm5yx`3XN^pdje`%%4S*W@{ zB7A8{%2|=R&*DpKPtMXaXBU~=po9xI_r;&Quc?21?9q;21q1&8WA7>u-^I(mMqgSQC>?z^ii z_=GdE;!fbgjgj(r%@6Z<4c5&Lw?;)v&2mNPprBx@4i8l+jk@T810wX1)TvH^l(!P{ z7*b~wb&+NRI71jDY>Qbxb0QIOuexvX+UT%iO%-jY{X?FfEI$P|+pWZkE52ueQA^@r~-anvayJp=2{AYmZf0GgT(!eQ5aJbOUxGcwX@7B^!9T$$7cN{%9{(?Ww^Y~vA31H|XPq@~t^Z%59+t335YHg$X2r}laIRU@ zEj&WR1WSlWC#8svI1&*n67n~Z!&#e(c;12tj0fT1U@&-Bg|u_E_xN!&Vjq0mHMCb) z(KdYq4Jqd;Ot(KfO!Z(8vY5fJVMCC~gb&qOa6V_+6*aSl*XU#+LOv{Jre_^~oKqw! zq1u}7*1ZcTcWYleWPvO1Oe!7EIXDc85#=^-I=^IU>wkBxxApJX-~cj{i-Mv(kweU} zNV~;s2?vo+2r>&|=KSqij&mdA*%h3S!$AlRagrK1J^fb=i#xs+eKNMdlir<f5Gr+57_i zH*b896?k;z>tEsFI<$(xcVT>)_4gm%uKp-)Z;iyBU1uOGKJ#>;+Bb3@puZI?0A2NO zbK2v!#-)9H8>&(pUurOXFS7E}2szciDny44HuEjA;qN(0{_ylhH`kb3{+Tt1boH+a z(V>S0?yZ;+y8h*vNk5fdotSuZQPv>R)xRo4IirdlFBjSwS9wtPN-6LD8S(Sl{#k=a zSO2OI6%54Hd-eu~@MmY7?LX`Mn9ds&S%XMd|Eds`48$H6(c~dV7ZlkVDlN9+)y+{^ zgGg6Dl|yfIi!f{e5!6Tx(d{dAIo5yjm_mbv$40KZKXWpY1iis*^0b_UK#n8~j5eo* zjk`DD9b2()@q>eko;{J|%wVvpgk+TY;9~#hPd7=9q>qtw&9{`AIX+Y6YL3XHohcj)VTSe= zaNrAul>cV$H_N~4aq(kJ_knA7jW~%cbrH^|t7dDlc#P=VURnx9ytv z>%Y`u=M&^tazr|;84^Zw`)>T}V64G+m&7%n*4o;)3$nyTIG?O}CkKI%vn2zpMmq~Q zk;5|Z)U7nWwvVi5>z|)5n?C4xr2@#BOb~vqiJX+73m@j~`zQ;#fS&$Y)%lpM#kpJi z*JLWgOloFpo#IfKo>r61&Pb1elvL$ht(1W`YHt1GX<&)gR}WC@l+V>Ul&uF!7=-$T zW57LQ#t~#H`GnYI*_w%M?3P5V^|t^WZ#Bm)+BYhbR@1NeJoRLzqu8GM%Ir%7rqQQp(E>N~xS#1+1x^j)Ya-(y52$T;Ndq=FAS3 z(8L~KBF=&k+#P2^TrfFGAxy>n|GL`fW;gMJITcr z*=EJRrPReUIZ{I7dEusf*vCFZ>@Sc;aejgjK3+kITPZP7*hg)xi)NiM$>{-YS+DZXXni$L3QBcBpyCHpw}Q7BB9<)PDCdX zRv`ptxbIwH8b5v4^UKqohh;Q~%j7T#jmLxcjR-g+01=` zO|2TU!I0Te5*pK8H=h%8^h3n_0ikj!hF}HZ&1*$(sq$>zs~>FsDc2`udXR+1_1sNd z&fJQGdyO1csRJM));SOnokx*f#y%V3#xasZd{_OZ6BmP5Odkv!g5hL>W1F884Ci4u z_!LS+gwLLc5Eiixhlq$gWFoyxCsWE*3QUQ~)MCBVAW`WhN-?HXt1yjJqfjVxYPHgU zv1>Xa*ae)xH#+>83Yc@yU2`8XaPA2m`5eLYLgsS>d6lg=@Me-r#$AKwV+8ezDj49qcu#2BsjopFzEDZu}-H}8dVZZDU&NOy#!ZdIyEL10|J@c z(I2M1OP`9YDdTr#-6niBNMBn zG92_whUruqPzri&G-wQJmCVsMRvRweJ3gv<<%vgbL&{)`MgD1im; z7bZ3E#)$NG@Jd7kTX3^!AQ&c16g-7c2HthYcR%VorS|HCU)x58F1>6^pWgOZ=&314 zJX4KDcF}$2KoU$tBAP#|h}E_Lo%Qiz;Ya(M>x|9@mrmjk`cIory(lY3*l(On^u3d0a-tepop;#nHyN>B>LNgXMP)IELi9h_n zA1nnFgVniIha2=VTnvT`sazq&Bsjn>Q_GYpTq;#5#Nchyt7USiKBb_bKGmDc2llkc z|8&a!f5m4zEy)OpLT0;gbD=)$eve!yBRaB`nvDQhFw}{~I)e%W6Oh;-mPjyz!Jsx6 zWKz9C0yZ#UVp2$`*$6($CE&!se&Wnmwr5c4Bo~EQXOzgqVzpGImMdinsX?YuOB7t%X1CX)kgkSpa9Bap2` zr83GuSAap^sL|7yVJX9D$Y?1;N7J&6m+i zC0DD!$Rfdvn2|yZ9aYQfk+y6jrlO1~#Cn}dY``Q6z0#nO7yvwt608WpvP!3yDrH7Y zj$tyXK`o~cLr2`QdK7M7b~Tw?_K|B>jTF;^0S}kSq!<{ZReHG;SE#@{CkI2eOoyrU zV2zkTR~ zhs%bL_>~;`UDg&TY=QBBuP0^}6t=SDhA9PwEh+9Y^~7F+!se3y#wdcqHj%Gr^FU#f zhr4nH3of^98z^kr@IaG2F<_vu{UY~_6ew(?_)qK*oYQjIo?$3k06oKG$J)R#Hh8~T z0fns!-;D79g>47lnt1?)tpi@M`$yr;|JT{qqwu!=YwqAV8+fnXsH5;c{ae_Nqwsdz zo3`AX9e1{;iyYfpbF#2_(H@$Ur6t>9%dx#O=WuMcq2<`7m~*H$+t6}s_scnq>mA!% z&Mud8==IZwc(-I}?+ecG0z17`#5$z=r8B%9H|*{a-mLB3i4(kzgL8xdl!u=7weC$i zcXsc>xy8Ewv$E8Je@E^}UnJRi<~(_Zx4|xmJZ)Cr<{LVUE`Mc1-^Y@fsmm5k+qZ3j zll&2gn*hynrpfcpo3y9|r?f#EBwr*Pd0K}f4_ppOJMxH3(y&3BxFLlxXlp4`vo@;+ zZFXIjL7OvAW+iV3fTndAs*+Qp*GneS3PkP~BLrmmQC z$)1o^8?#GQhvA4b;iY6B9qS^htQwPPaIR)f&Wv|B+t^$(j%CqoOksn~#+)fH`+{TV zdC01Lm<9(44mJ;G9~(PCgffekUbY46JyR4|9E>5Hj7$YPNbJlvfOcRN#nej=SV)GuOQbX>ws z&5OTP_^aW^@&|u-IPz#6tnByemP{b-+h*p)=I5NZc|%hf$bJtFsave!Hb0qGs*`Gz zDv3lVR*8+kEusUy8@*bt5o2nZ*eF+N9n`pixr$UD5y3>u_|XaO2db5RO1YN)rlr5y zKpK2UwSG}118y-{Mp!}!6zX6mH z2Au$XF7KK$%5o%Xd#M@iYhT*@{#y_)0DjROCBUR|iCS$`=ybrhj>&MN9#_b8AS_3w z&?!OWii`GArf)w&dzt&Xv!uRMJxXWWD68M_Tf4YU%}sx9pV0W>EX14PdM!3mKZ^xf zR;-_%SWcillTx(!&h6eSN&xqYmuh8**+eO3Md_TP6^oW^qN^QxHdxeQHR9!ny%`>< zo|Xq=cQ&d#bYcJE%i^j{jlAR40tJXKyQ85Ju|b1_U@Ea*0!|?)RxSDbWC@F?!kGuf z4_~QS>t}BkCjgx8wc6r$k#7LCw?ib=b<&|Klhzt*$h!9({J;6U=zqLk zumE)vw?+Yp9)xRyEj^|H!T%Da1d~YRU~t8h3WX8`nz|0G;B+8}Yw8yiX>xC4@2p1* ztY|^;(f!+w2F1q2ZQU19;8mbk0|pXBV?I0_64kcc$tQxSbf|oKXMK^=5TW`j`GW z%~sW|y{$nnG2j|-@J9(81mIka4mbx45;0ixVk(7Rqj%9(U@9fg{=2=xr7MynL(QiL z8zld$o-I07@j{FF`!9PjTtd3Wz$LNjj+GK;YtQ!R|9brIZtco)Rl1DwzPQ%6YV*{@ zvZq%(4_~575_mUaLdKhH?T*wJ-P<8foAItuyS4sqy>7S+*BgxnrBsh;3^D^al>$yu z<3_nc2S!|-RP3U`bgl@u6_~0=>hFn)rk1O#ogAC^$lCVy{x->83zm>y>s8<>KRkr(u7=;ETg#E;BlS#B0s5fpnkA=n zb<-?Ut9^)?K1k~Ipb1GoiN7&?=Ow?CJ?RgF%2v7<>8%k4dJKf2$P9D<2a0T1+wEBY zl8biqZJge-uleFP0Gp8ZwxY?uzDqn?Wn#jIG3!#2*Slr$$wj>;g0C1+AG6IkcGm8j zJRrZtCpoq2B5%h`$hl?ZJt6OO*M@9+R%z+r8^w7UUOIjlam*eGuC1v$A;IE{Lhb-dz54U~6k~ zurK}%z$T<^s$KbQ%&<|drcF^K{OZ>-^p2N~*{1Lyy~Tj*ZH&CHt*@N1>0^2&MXB$9 z%Q#lN{5Judkobr~&kERA?A(8Gz`}~Y50c(_tD*pjgM3XO&kGs6i*EdQyHuegXRLo# z6+E2WRGaz@z$T>KFgR}ZFE1D0>s&5TJ@R_rBi=d!5_g0#qTX8~K*0v3spk=x&k%k{edO#mk(9&#!%dGWXtqZh0nq79qb zq`tQ<`arHayTiJ0S-dJ}-Eom{{=EBJ?oLQKUurjEi<>Rt_)kT4tr z?v`yJmnF>TF&ts-?U)HU!@;8AmZPkr?*D2_8o!H%%G++g0)K$3L?O0YV-} z6&6e-3Dbw?N8*?!=|{jr;b*X)B2E;z(vl6seZNl8CXYaniOx6-9tsCyEFVDusO=yl zn}b6j(kZg!4FV*VYR-WYjy@PZl5Fuo0X8MsDD3y2M^PqELXhcj954|z!b9Ou-bD{a z5ps8Si{O0rQn9_R(#=wt+m zr*0_e#|~)VaUvRyQjcR%4K6*&fD=qW(Wf^<66p3_-S|9?AAiAbWIq$SNw?2&+wxG@Z~K<2wNmnl zN+c`TWjbv%2`#;|L%wWhB@czYmHR0A#OhpnvVGW5c#&>D&><<7i;joFKDw=ANI2W> z@HzApe8@lcl%h{xEO0zLSJxB|g?&?t#*!?am&~jN$pm%_ZKi4@H$oEWrvW=;cqr_V zc|z4$+CK9-Bp=urGHx7+EZOKl=IVyvar_XMs2bZg@H4AIGJ$;nBgRt?{H9YES>|^h z3ftc=Q}hQzktuZN9hPq%3Y)&;CyDZF@WvHtr3dMDmza zKl3^yAK0!ohpLmVbKqiIw0J0N(mF=buW`Pr4tlbE*qU^QZojibmt5^QJdPn}P7K+L zl|N=ygX96*P&QCCl0%V+AO@a3wZY>Y+jvS%WvSI1WMpgbnTc5s$(bzu1s{o}nsZE1 z@QH=}6lrQvHYM38d|com-RK<*{=9!k?t2YRIh}Q1f{D!g8dPK%R4IeFB?;7Q~WI>0p3^jBE}z!aSwO z($jrN5rO6$AYuE*%*kYn+bFTqlw_l@0hsX$`WS0=TUPM3X;si9K^nXJ4OxYhZW|Fny0@k-uEbB_mN$v z?;<(UO36vY0N(>7$O1Rd`p$-M5lHCYCiV#E(zQ*;4*evmiDUvu%nELoF2>u;s$w$R zajO}ewhD;~7y5(K-;rVV_Qb7kByHCST+ix{Z}HmbuwqRWZKwUSKv!2-aJI8mp~-B- ztz=ES6S!Ry8E1(gZpO70<85TI<4TT}T1SeS#S9kSbh<%C=GJD`AX{NXFvG8xzs+t1 zo|hIDGq{eok;xneE(wTb$PKtIG6-?hCco0$it(0z7Mnj&m8Ru9%w!h^G?kESz8xNg zhw;GtN=iV8n?bY(z{dkiSh{4O9XAt?59|+ZJg{=6ijClArjF(StJPvfVjP7QYbf7I z0={*m$cyooeHML7Cn*Q~Ja+4JdqR95u8*{1x-guVl-chA@#`HVHcO;cj}s~YcFQ{Q@^is&5=p!rvj*XIPI^d5+(oMechM51Am^#oW~YA-VLr(#PpBlK5~0MU z)NwwPy2I+(-N{e8TZ8_6dH93eH0VGdzDK-?lVt=K$L+E%yd9+w?`HL4`}7H%hWUNp zHC7i=?N|OSxBE?P_O#Ro)N58D#5m+(-Cu8{Tq<`j{@9eij;?v&0b^LK*2%^?wFWOs99p%O`Vjw>+dXFA0t+(WhT~RXPO(PeRu}~Q69lE#@Sa8Mq`;gyoh_FVt+p`)*YxytTe#XvATZt^iL~=eY zwY8!@C^Yn?t?V);4J9?q7;JU#Dw7s1nDn7@-N9=$)%e-Z&Pv^vLghfs>g}vl4pivY zRROwX`G!Ni#-F&>L4WYBeb*W*|4YVNa&17|84IPH&ip1;E&C=`Ep?fyHmf(Wx>R<# z)^m-taz?8I&DKZtTv7Ad{`*1vyI%+i^$<04Wh#R_i&w5XfHU=7z`yh{CNHeGwD8mF z+vC^QSUZ6K`0IdY-HFQJ&+5IZjt;QeaOuO)75QiP{OM!sCsPMidV79pM?Nd9uUqpr z>mpQou4a`$?>iWSz)ArA9eVMX1V(Q=v!m*xsQXP9`qn8C$oHz;-~x2s#A*Ex|1Y-I zk=B<(@`cP!$${a3x(|aI4ziUUT!0!H{MOXAddD2W;|Ehd_Ly^>?@jr^1?b5eho6fo z)Hq)_sQHrRe`z=7fbeoGLpZpP@yX%dQEyiU?cP~SQNLY{eK~+G$8v;&yBGh6n71nJ z=aY+n`0!!j%0?kMfG)?fgag^_-8y2(?>+$=8cHwT>icLWdqDXw^Mnh~j4FMe)Qau8 zir>NC_r%CEZTPvjM?sZ;x0m)DnzY_NCrZ6=`u}2cZHI!EKCi}_x9{`9r}p@g+Kd|~ zb7y~op6>ipiTM?Vl&CXl#NDC0yM?*W)|1@{T4MClGD#&H9xHLO&C$M1%bEVGy$M=n zTn*9Snyaf0Z}9W!1@W`yeAS%^Y8li!-=WlI75DvEq1T4y zh@!kxl(U8`i?xw%qOPR-#+L7}(`5RI;6}%NKsKwMya)&wj-oK~Qa9k`2?vfa%#Hyw zXcQ(u&qoDJ+gff;Z1c*J{?!JRZHvSMtEvbhJDweudf;%fm~pe+rUm^RK=b_=BrTEf zqQOq3+_&uZ_fM80GYP&f{yw#$!*tLY`j^^HeoTS$9DL1xU8$Y9RLcXJAD8Z9tA_rM&cKS}S>_RZ6c@COh z`u5SrLr!eEuyog^amTtM(}^BaJe_pNrY}X$X0Bl9`ntXInI7)KgSYNp^!s`Fd$e1h zCv8J67TUbOUWTPX;Lb>&%%gSPzTt{=OoEFRNwp@x@J4}-AX8XsOOo)=rMzLgq7R;^ zs6SybDhKcG`C#sp`0L_C0dJyH)sPrM*J?$ga07-G(f_!iVkdF?TNa_n+e|MMtZA;cDK6&o^O(o*H76`wbOrVeVMJ7>Cm?m4?tx3Ve z!T~{B_63(wJ^k=%^n8MBF$&nk6^*e>n|>(dpl_1Vv~hp|6a|dhAGa$|Ja+rkGp)w2 zJ7xhz87H5dUeJ^;*s)|qRdGUskIxcj=nBHkzy}CIGN=)&W28MI((a7SzM6HNrlPbR z?P;k&(WcF0WOeESz}E51&a=H_(~6Dum913-Oo{QC%U}xzd5du?4Cz$%AyEypR5CLd zi4j&j%4CVO5krwFj0#!@V=;2sT>!-0b%X|kaG18l*v>TUM-`GS`w@N)p$@zq=!?y5 zik5yoTGspb=%^;mf6PvU7`Elz~zRG$sPb7~d_|9pslvt^NZxZNO)vJb+ebzztZ zB=Q=hkCuW|+EjFo?*iV+OhX}bk!Ax31_=W3Io8jFmLTp`hr?jXPXSYjHJXqhNW?@& z<^1`1IDJeJ0Z^6k{sAr9HR~4OKO-D7n~dNH0SIRSQ6lyh;1)U?ZWoN{-mRsdIxZZy zV}ul4i)Rp=2^V)uApUNqa8Ml!kFX&LRvhHLFhuH2AgY7-t)mqv8-J!1%=1KW4>805 z-*{Id8pUpk!jX8^FT^M#|}Lg3E7?9*=Jy(5;zYn+`3fyZ%o1J=wLC(<^s-?~20Ff2W8E;GdU( zD=kL`&L=MYI=q37zJNoZ@^59p_lLuwl?z{8JONlPRPo6By(vz2h7&Pd~j~Qf& zP|EoN=griTD*N0W*vRRLJ+%|dRsz`|IVQok8F$D0?{uXyjErJN1SWgw48b96#FjZc8KN=FM^Q-N1sJ3LXSKlASS|t z=ttPKh|HqGD?-Si3zwh@k5G9Q7YVx^NmQ8!QM&J}@+@K!c1a>LKNVf+J|a(?l>&~p z5}Em_u*?6-k160dGLdKvk0msvcBxD+^>HZV+!~rVcctDYvR69BJ?s?)H z6>yxRZ^xr4;J7rABqjtklVHqmz`ZQs_?MAHrbk3FZdO?4H|MF91L)M{oz8a zUztte6NYS^FttpChK5XW$U(?C81;I;uVSEY;VnK5f8sZN*r32N8ZyPf0hu-E{kNg8 zm40mTeS>x4s-jbUdv7;nV8}Fbz#yj~X0#}ieIFag{Gw$K9sW!bzj<^>^C{vO^@CuPd1gDJ!Nig969^O|7dT>*&1el*37X3fSe8B zx3ksb?DW2Y%^YVh_swnDI6F4h;^e%~1BS<=D0dg?BxyA>_Z@-=OgJ2EA}|U;N4Yn)=trJ}q6Y zfPIcL-ayKWVaJLff4l2BB*LnIJxklZS%)?)1G;t-wr?pwyS^xw+T>ZO&fTIeJc+)k zGb58&Kj#P!4hv2nkx7^pTzEi*93gIR=D@DYMn(erQNf^A5E+@T^YgNuWxDlkYxJF2 zJ3Zj=ej+4n%lS=wA}Fk8F%T3M(}2fox}C?XUvGA}H7Z(amMcOB1qEAmc&JKg)I|>* z5TTExg2F1c%aLqLc{!W%ayAiptUz|8yqrxoJUcm?ez=W@$P~yTg+i^6sYH||Tts9{ z5s?|2L?EvVQ7R%cHqpO_FrTD{jHDLIg))~?%0&!H2|Kg^y|ubw$CxLbOU%60>t)Ix z;ivf?a3+EY2p*200vFDXQi$_G4Ey0(YN1kF#7TcApDi51zu{(|sh9x~D|EogmsJR{ zt=cuZg}Q8~{(lT^xP71Ek5g{5rHnHoR(ybyEvpb>%gNRB!p+{Yef=W#7wB1Yeu98- zGBsUr6A19~ix^M8v*%0+#H~siubS`5Im3~Bf(xBOpBHvp+LUpI&@LuRlanW_5Ms;8 z(&T|w6UG^WuftvM!q`y?apvS`y6dKR89%&;`2%{sF2xY@Ma$m&Ei6)bhjMJtf>Ynk zyvcv=CUa+gCJ_jxB1ao4Ka+_1ps>)McXrtq@-rRcyAEe`dPtzh`e589a-K3b$Ug_kI|~B{@s1K571A9Ar#lFtJ0M79La|UG zQmAxljZ$xv%49euRjPGjxkPTjbP}0dsn8n~N{vw?!!S9!t{+vG63x~>ijNOUD3nl9 zTs=d)dPD{O4bQNKVv!)JDWSgexj)iOC$pHdxApMu@ISt*HN=!OxJ;+KmrtJY{PNK2K?F`5o? zpayAxNNH4w#X5ruQ>!qsK`fDA27^IuFvz5Og#;{Xm1>njLJiVzd_*QWy|O)pP}V&P zT~GvB{JeP)e1IP)@Z*qt;2~DjBX(8;lwp zNLNFFgF_xN$>^1B(NXq23Yt%X1CX<7QDObuRMyXscQK^hF09vLL8#Q_w z9V|LdLa%ItqYQks*2!_TQY^=%m`0|@F`Zb0>+~{A4@9O`$<-?GMoTawW~9JDVGNn% z^U5|j%ECv1)9X}X113@El?IK(0FY>uVz9>2h;@3YQf9>D7$%b%)N%?O6tIvE%*fp;GEJaxehObeLK%Rj5@e`k}f6CuEY%E4Oy9 zXaQ?#rz2sNw{+^EITtw8zS)CSz9)NEw7CCYR~y~zCVnud!i%Gw*B{S~ohxd2lu)Pw zQZ;G(l;JyKx2x*A%hZ#7D_U&AD(%hRCx@>r*=EJRrPRg$n_VmVc!6qZ=><>c`9w7t zB2adl`W5%A=(SC*Jm|5qHMe@W_EpZ(Dzz{VA$W__b|h z=+eu!^yzJng`S%7HFu}ztdAE9KkA?KENRVm=id~sw8d-prs&SpRSDKEeg0fp2A!2s zRz2=p*qNf25A11?|LK(d|BBCcT9OeGGq>Ilo(_-H51%B`gwTl@WEbVrCn-=}FWr zJObE+O=co|LjF%tzR%N+Lduoqbk`+i)-LxEgaE@n4V;Y6Wu2uwe>0a|p zWuJVN+FV4If9A=#Rz8n%8nyWH@}ID{&b+66BC-aHs{IvUF?#i@q^_=FzO?A)&o=5yk7%AhYp|%=Ujde! zR;{!Lm$ujXbL{o7$bz+pJz8}*Yp|%=UjdeahV}BmrKN3mDgsOu7cbwQ`hzHIu&CN! z0hW@6)%%as1J&>RlJR2hh>x+-O9itAi>kdpTW=zfbchBqWMTXEm!ITs8*?qG#`CcC zA)ihPnwl+^ti(Cg(CBU|&?DJm-*o_yJdPYV=#C$$D@uNTR(5FhAIQY{hj*wrsea%p zIRT(&Fph((D9O2@&(_u@s&u||V%oT!J+7?&S^o(D&ML<|kpRfTX9)`fcWH{+S%OS< zTp!$0CK1yRd@kfGmw!kUUdD3yjUHX$i!7i9-Op4?$sUMaI?F)dX`=a0^o^X=Xx_eI z^>~l=7VZ4<2V@S+pH1({NjHt(ZTE4;mQtZrW|F+w= zR5SGAW_F?_C*598S#<|-PT;arT9EjRrm@#1G~p#(J09Jnbw4FCgAgPIicNXgeRd!< zqa9==q*j3R-!#`L*8*4i)bJm2{8jHR9WMFLLFN)%KUu+p0mRW3F=BOf<^F1Q*(ahh z`bWM8%H$i_=U)|-OZd|@PG_UkIXoxqfTV+FhdYj~CR;6b!eb2jjbqc{h4m zO*T6O7PB(vHOulq)vRk z8uEubJ8`sL%V&Fgl$vm5O8E`94-~s|IIHQeH+_K%nX6AkrmO=t0YakW_WzSPuX~Rl zS0nbp$6Z5vg%xenN6>KPzU}FYByaP%)*K*et}Bd8Aa?sC0vT>{v8_}pom@+;!Sk!vXMCF z7M-qE!hhAUxZ`WlCu0jd>D}r0`3Gd%3ky0)L9SHtQe(7YJ@Mv@BClt3einQ&-yiQg zyd+7N7I2cv7}8mddW|{~ z)=2ExbwtCLk^i%28iHS!SttW}MqikY1)4z_F3+;@n zJg9r6lz0D(_<3!AiugzgHnD;sUcF~;U1|F68+UKQJGNrq;s*y6J$q8LaDwze{=LzXU&6XHS*v(+PdhT1 zqB>V7D+$t1liVjF$W-S;_aGMZI#ie zWb#Rp`f4F0mRWVu&6b_LX14WN#G}(cj!aD&T&LxN6he#R%K?xRt{r{UyYzEji=78c zR7@Q5I7|8A-&6JB{P5K_==nD%1YT{_y~=lQ^J}goyd&6b`X3U@Ea7N86N&`uI}9q? zyXU`2Jq5$ndy`4RC%ypBi~DGE*{Fyslb7XBd6e+O0N!|J!a=4I;{&sVqFWkLdcpF+ zA$`^3s-9Ns6Mwxx4J;@4$g~#h1s6>{%6xFK|MRDtBuCQ6NV?`*dWBSj$9!aVrmGyf zR{op4-z@*G$Hk8^-3PASHR2?N_68rB45PtnT}(Ooy4X2c%{`vW` z>4T0}DnQjfT7Y5cQkzknkFuZ(=;@zTosZdCoV&Gu4W+b~e2{q8bpS1Zq$=lXr3}1L zbL$^Z1532NdSE&ExTXofzq@N;38lF35 z-?jyghNn8FoWIguKmEj=9PpI$H%a@YDd!AHm9;5n?vam&o`|zJ)bF6LXxvnbj$02}zxWDQ7K7==qeB{4yisCHG1ac*@!LiXh9vlrw4ivV>>i zoS1SBO(j~&>XfsXR0DUXoLwh~hmw^k=R-V!#?2|`pQIZrpNc)5a$bFuH@Sbq&56;=)E{2FXt6i7>=pk>kCQIHCI*jD zH^>R8X-cO9M++#2#UeNO(A`$_DIn$+IdGv0@VHy7;Wj^+R;rU~lq!itCsv7#;2?kw z#9->xa*Y^M%fv>xO6#D;4O;qiqdp>nImqZo-WwE z3Y`v|?ZafaQI9KRI=uv!DRfGsLE|!b(I*Xv!HYPtEtCi)SsJ~ldc>e5K-(y*-|$q~KJ;`Oh1CUs<)u%Y_OsUv)=QRceDyFE`4K3Yl0h z#xP8$$Bh`Sk;)`WnN)6Y8Alx%iQFDYnVQr{n*5V~pYY-4+kei#-K77~w+|3+hwRn( zNHq;le4BD|a5w)Jzg|fhJaeR%ixfcqQo?D6(S|Vb+_VzH-OsPA(HAk=}?tPYmGHz-TMyy-+W&5KVC0bfVzoWqku%OSL(o)9#eoULlUI~ zlSt)YaK)4gg%af8a2;4dPCalYykAhH$-RxevmP<9q6NiA_isBI6dMz_bzek*SAkv) z7)TV2`S5T^RNHbVp9rGXHUF&?df%O?OJP)l6f3wwtO2L&mEd^29wdS>NEDz%u2O4s zE>jmRDTA$_&7u#*?H6m{&VDoC3CS1P?o+^D`(w>k zb=m~pm=ZYtoI8@&8x4A;QLIp?q`>Shmx6pTxLSv+Bnpkfs8V6DGTvlscci}P z-VSlvjCYONt@U^7b;D)2-e@!^rFu+bkQqS2E|5_MH_8<{FyiW@Viygj6C%56TqK7bVN+?%FOH zBNxPve-jwH2j3C<=B+p5CFJ|da@GI3w_MtFPM>`GedWqFbxRr4QiUAXgQRmZiCiuP z`3-Re$Tg=is3j5wSii|!G?&h)?Y8Dp^+?UVshin7c1-)ULq4U)whOBG-fO`UlD$4o zVVrb*)wx>LJNGTFo-x%eO~^&bGMZbVb&VcpB$bMGScpv>zA0aZH{&Jb>r*G{pRK8H znxy`9dPKfHlR;7(?~E$JtdV*qr~rM?bQL;2w8xL@{~cI(+zx%^e$B#v`ZkagGRMmn7?aRx zowg@MjDI2M-wKF7l*&sHAYmd!0jTbx*78-@@xb6OwM;>bzc+=K&zM+nZv?Gbr9W>TY%40*MF_4~fYS3#Cm zd)ut=)=4JV6ch{$(2S^CZCgC$r}yUarvqDClY@QnZvZwSZBy;aZ)1jyYBg<&BH>rR zmZ5jNbj&t|2k9*aTyJCKeQkZ^j7=ZYD=A8S|69hf;^n^y;Dp3S6na*`zGCP8ivt!` z^nH-@&RZ1)NE}=Y0dm-r(c9?8kGD$|I&#MPXH~(&$xXGX-vDev+6{x_X8-bX@x9LF z64fKG_dVjRBOr19oe}lk5+O%+idOF^uV1*L<=zK;x2$!!xLyqpV-Rn-mKcBb>Ad`= z3H7Q93*5i#-G~Xjm@Kb4ym8X8TQ`TkeOX{x<=fka)~yo`ND@e+zKhk^}`Y=;^cFcsF;oxOdvvF#*b^p8Ox680M?b-06gOTY5 z9Bc;6WZEcylND@tE!Jq>Uy%E`1uy_=iG#daf;h;kW$q5pk~w*_Kt@_C9)@E!9Eo9{ zpx~jfZvORnO8j;4@W(&12mw?cMxwAQPPd89Wq@$Vj_MwwYerK}I$Q$3l#_MUo|N5FoKsa}Jbn7{Vfo^g#hOCD|w( z0I-E_bP|G0hXaC%SDuH$F}=G|Nf_~hOe&Bp;5gbeszUNGWU^~;HjfjXz2-34_NZ9I zg_dj<4vdbcnoUp7k4$t1YVlAwQfu-JvdxTE4l=SiI7aF?MV36~J0zBB&LLemJn0oh zIx0V#l57+X42q$iZ1Q9TiKo(R^kYXh@Hn9jZ>Yzys0NpwWB^AMOuS8YF1;C&K)3Je z&*yO*`rD`)iSv=ns*p@zXZmrP3FyT%hKtjW%|l^FcFY|T)bUeXda`}kiM*1cPyP!* zX436*{I@(5cHmy4YOR!fq7ump_MJYaDy4UJ$d~Q4rWgurpu{RVDqVQx{owcpeHH;_p!O2Sbr5bmtu=a2^WVz{lJt@tms#5tB$@^YcuK zIK8DqM6lr(qz{BFzuT!glj>$(hvWkrT~nz#$?Y8~%QkoNP}tu2l*T3I(v$7OrpABi z_FFkH!A3qF3S0RWJs<@&sea~lNItNEZ6j4DUFX2XHf`}Zwyld4{Tk=1>Yyjvhs{bE zbo-qhy5wrm;c+ZFlOK}3SovdSHAo(?F=akgBRLeA2!i41;~PBA`HdAv$c9s^ImpP? z;KLLDP-N*Z_(&|(oMVcDk1i}eO0`&&O-VKipBqS_DAVsC$Rt+4J`aT>_Gca=o7`(~ z(vc0qq4IApk#xzc!IEM!-J%009HqYhGD*DX2NzPZT{t{E^*TwKzOWoAC2+qiwLXEf zx7?v*elWm6Mm7gqW9I!sHJ9!~iU>6400|pH4p5}qD6!L&WTUVN;u1xf+?kXSlP7`4 z+54}4L^5B~#7RfC2XDF`P;|+oeMtEb4LVT5+ugC$GBychgauM(NXcg5eP$v>n%o5> z11(a}$5^x5vVyNo+k#H{1e_z%V8%3+@pz)^;NJ-1-|WyqSd?_2E;>YkD+dNe2M;ou zLe0jg;D~4*`JbR0Z=J;&L}w%ug6I^X9kYengv3=^ATOtoNX*%S`n#V$4uar7c2)!W zVTJjk=IJks_dQD3ePq|^yGV|-QgRY8!1n-mb$|r5zOx}*1QPnUi9G_kbZyhILqCaX zVz|W+8HQQGRS?B^n^{#%W;<>*gJV}AQQ<;=a11;$%--HX+cg5$v-;y(yf!+lSW`vY zY5y$H)fE<;?QB(OGK1UD$eMU3%pQ!4vqTUHbZy0W8(HkQlB1>8k)mcXgM~MpZjh0= zwV5@@Ru~b?@ayGovs=Mts)fZ2vi3GInZrPq`&fqDfa@ZI5JzqDE6uGKZ~14j`4d%X zTF%2vc40tM3CZT$;Zb-P56rKm1cbO5gna;fJg|hNO9t9;Gx7MqM$yItD`%?Mh?&AV znggs>ixr7+6k4pId?yL`){!DF##{DT^evsF9Psnlt<&uZ@qxHL(vIoEa9&bozXKH^ zWM0VeBcX@XBX19Gz-vLKh@ZvObpA-IBQK8miQeGy;;DqbgbILdvyQx}MDUwL5^u+> zLAae$o^Jw~vltKu|B!(=cGjIDbzvsG77Pj?(wEv?hhtH=S&JJYiBhc*NZC&R9>RS3 z5dcYQpLlrfmMJ8GziSl?Fi?0KG&pLi9$15@IB&995ExfkVnlpyd9+w z?}Hfj!?V;vrM8HZ{!TtyID~)8?S506JuUSC^_o=(F|OJ*x`n!Ir~ZEoZn%A);*V1v zFsAnKTIvI0%qoN!kBVPyoouXAYw)tfp;c?C5Ak2Q-D5o#j$45R1zhE11?eN5UtkdM zPY{$|!+RF3lk)wQVZ*kSS+!)3lz+)>D8L|~1^*z%ObNuVN*b@4@5(vDk$i#+okE`% z_CV%9!NB}Y=o$EcxUvc%#v6M^ZrLAmJ`hQMb?sF~xY;ueC#GbAGx5n~?I?wKSK53v z?Cq9%wGIdLkH7A-umu0U+d^iHT3Y50h`UQM#D3ATH-8I@RNkQ+8?@lmcQbGDU%JiS z9&EB2A~35xS_`f&A&q;~)=Eo#P-x^rrBLiAXO}VQNU34QV5@sqnY3uZqz|3z4qmIN z#!vY|93wgV;x<1i7~sN)PoYFh%m{c$c$k*>pC5JUyjCofOSJkdURTeq=||M0&}~`1 z;ZU#fC$4qSAG~YdwZ_W-lGQddbb3p;(1_Wvale^9)J9x!0&Fc>`imd=1@wd% zlZ|SzT&xk}N|{EZ$HjW1N-dR34LY$}DVC@d5~;zUk{I=Rm&$|~97FdSY2}Po2b!&q z>baukwf*;l_;)>!X%3C{MgG}6fBM+^$<#rW-kx9Dk-y*B2e;B>?{pz4#f!YnN%JzC3c)iJMWzj=s_S<4$CD91FK; zp#;RAQfR&1iVJ}W0ymomf)U6>fl&x$;N{U`ay@2Ls|*H^uTG3hjS971YQPkF9gr}{ z3Ze#I4FA{ObwEXNe)}D;MvY_dU4zj8v$MOiyY^r=CZgCy%*@WNA_#_JMFmt;Komv9 zP6Q3c9$VBX_J+L@D~b(41zW7~-Dz89$$56EdQmO!{sl55$F-agklbQBb3)uVp@VUKtW}_R%O)c zFckpARkRkPOj-lSKNB-C}b0?YYwwC-A8dDigwa`E-B zCJ(WVVRZ{MHSfNu8F&XiN!}5NKKlQHUEf zN{tQz54umTr{`U*y80DS^M6Te8RK-S+l`SWM=m+>1qLD$4eK2q9%WLhFiZzk8PtSI zgVQQK)O5jclYufq`5Zk3eY`ZPFWoUpSsnxMc1JN|Q?R_I>mr41VM zcHQv(sZN?!J)0i>0_Y4@=m@nDhGw-9C-o{Q%me+1kus>D@{vie)2g9jkH(-RO{9rK z$9YXJY&ba2DaK=i!%v;coD09y-J$SS z$LM3UrTU0VPVf2WI&SQ_;cm%7TN|Ulq}7VEm%q{o$#>IHjb<(zfc z)#CqjO^oJz6@R|^gp%i49eF!w(%nHPrLzr_0Ld$2RCB3U|4WD4dR?oW>0RHkT%}Kn z>Q7k@qnS=UE2MN9POV&6J7He7M`U5HhSBhyCznOF4|-$T+aV|uyE(mx*23tFp}AGJ z&j|kNW25h9-)OjZoQzh&Xw$7-AEbV}-DRS$&%5zY##Jc>>tJ-)^xrBRc=zh`@D0T7 z%2;e}@mU398 zVDG69YmPon4{jH%{c6K8*{y$3UHqgT!9FQB0y7r$IOB66R;H_8bn(Y)<)8kt^!d_F zrT%_Xx&BW1u6@zelN;g#`V9Ufr3SkAL`~hyf($QTYUPXGIC`LC>1VM={>3i4|Mqa? z7+I}*(dIwQtdY5G{FZhDU3^Bi(Es%LgU%OQ^`gfsD5|U-?N-q(A?umTYquOZt$ER( z$1S^e<%gD@S?S-nA8ye0)#nrU#a6tin}f&5%}@It+&{Vg`MF(>pZt7mUu?aLzW#7E z#<|9hQIR3LBInf@t^a&%Uu?CDW_xMIl_|foM4KTF9UiwXyT)qJD$2DkTEAJx3az(b zja)By4cbFLB+&tPY6g^;-aPw3WA`b|-TIt2AIn>Iq|KMaGQtGK65$k@^7jt{;Q%?B zJ-B8Ll@*{-ZrCJ;lV)bK zklDe8Lw`6dfStfGq$3*GYRNCz15Woq|IT>&!kF0ri32lNk=fbLc_eh+qy7DienEkr z@SbD&=YAZHSBZbwI+akDsk!HUUJ9~^d9KCQ)olBv9^Q3)fIt868)5UlIWj%v=(ES9 z|CZbDD%m~UY!H>2>PS2A$ot2$CWPx#Oi`- z9?ARicW3lR{TBYy^F?&soyRI5i6SIomoE#2yJejV#?>D6n(q4Q#xGuHw3S~btc~~& zC>EECpT~;%mUd6!B6SXjYwJIKV#qdSd)kR=QYg0-uS^^GqBU@Rj{=@j5S#%R;JBkHrNce%43d3SOqU93>M zZS-4YwJnzaaQu?6Y%j9sey1yMZp|A|_NVXPEq6SpUTGMZFD!dUWQSRyC26EYdqlm@ zefnlm+1wXnlLmJ&HAMRk&g(JqTDd=ueD~15^$)4RCGyX;+F$UWhfB6%)nvv%ZhU4& zYQ;1l9Er6We_0dA5vuytvh`6{2S+td{pI?IgPqQ zTk*x{oocl@K6`82+f!pE)x2M|#HC+Zt9xG|QS6AxKQ}xWd(^A*)HN>U-Zk@1EFHW3 zaNNpA*!t2)tks~#+n$|r<*S-u6}FC?u=i-)c&D&cY>WSKeY(%;t*O7~j8tE|n)>_m z*ywI+XWjhm5%zl}WF9w0aRUS2a@z_-=0852OkFAt8vA-}j}}K*<1iEyxo5K#&$?1i zemoSAbV%QLU5X;w;BW5Rk@*olA+>E zJtRixIeEjRvP~Y;-y2=e;1<4m{Z{v-NgEm9`$_|z{Vkub&vu^VHXtSQBO3BueZ?%H z*Fomn^nH9&ZUgl-&x;_hzoa^jx`Wq!u zd;cya{7G%9?%rU|`P~zpn{GM3J+eRiiHK=AaAft+kUXkl^Y!t@ekFbrCh)_Jejot^ z5jHn(?-d+0IylIZmihnpfEWHf;Grk8niEqxFIhO~c+9Bq*nek6G2hI?-RwQ!t}*}H z7{PVfpz8@V=gh)HCP0BUaX(z@#?|0W4UOF+YSxW-d^zqw!X51C*$WPg)Zg_1Nd1$W z!T;(h6r}r-Q2}Gd<9=$5&xGM40w|-8mM~G{$Bi9r29bEf??lsU=10oNV)|JQZY9I|GjDfD!7i_uem2hpk#)#{b1{H16 zskIs!igJ)DlLm)db_|ro+DN;CV%6rhBDIUk**|bxuphjCKp^Ss3jc7m{ENFn-4%W< zi=zuY9{%l!PIo?VP(S3AQ*?Lzq(#4Eu0!Mx`mqFBO{5l6t27#oTCLU)Y8=!Bdbq1p zLx98zlrF0LXfsS-m(u-r`hR2R~EHx@(6A&lq;)d)EN>W;v~p?)yySBdfKSe znFy^xN9!q#K?TaJ8A~Q5r_qX%nM-cA4}TZiP6FY zD8y*CX~5{dcf%i)-$1_H{b&^_DMkw)@IwiGlEFZ$)Fh!(VT2BnmW&u=(GYr_4wSr< z5oV({G0NwLF)_-gh81i<#aHd%ONl`4Dj_gCVqT?j2}grA-ZU;t(k+cxh7=p@_hm{l zTRX>^V(q1v1a*vM(3~zE-zayJ5|o>^vV%? zkm3aYo=mA`>MmK6JiPVYZ4GvQ{O9WS21}0TjH`hZ58#i!6 z^V>MI-S2mfBEOm|6Ya_`o?Z<=%*=_%Wx&JMd=zcMBj$(rGR z9$ue3wba&-o+DpeOB=s*vTVBtTi}mm+C7*{h*@)dI5qc9eU4u_wXX^>)z{im~2V5!!Fk$!(98*n&#GFZ${SEmP{I-vdop(^XCrP4tV*X zmn+fc+O^h1KSvvkIo|2;nr7cVoAsgjxiYdPIv@0M9sJn-&6?yl4NZ}!m$`c0icOx> zt@g2+vK{S?%ShUS zFMPmKj)@8roz*eXfye~kI!POPyJwHz>O?F!C+V1|NH9wg253!6iXwD`N~c!hB$N^| zXmG7T1NsIXLFm28(lQhgYAI`WmC1->7^%}k)gdEhg6efpix0w9 zYoc%pPUmd;Q*&WR>A@*5pQ1)+?TWd*#5e`CG9m78Ez@+%fbdJbOmUq z1KrYQ{XF%qcTD4dHceb4NiteAprypDfl^Z{oY3N0sAs7I?Tu22X>>-YJ*Fg#Fd49k zS%blwsq4vC-z(UeamT)WIuCQ_N)&eN*xkc>l)!AE-GLN~gCk)50(- zhMkwzIv0U((m#&!f*kXURkZw}P_}#q}3|nC;u=W!}Fn)!mP$kaHxh#EKBPluiN%p%bPj zl_nU^aHUphAW0Kb0#gOWFljA~=Sy&x&)~PqK!M(pW8RD$)uOEbmiphmt)H{~yrfQ| z$Z$*9euT=xN)w?5tuAJQc`p2)TCIg5x0q3bLrGLyd^K_4HD5_27b$ir{jd?r^-`FLBs7rU zL+A-m@ajnut%lOLvYfP(dS5*xT#Du6bh#Gq)J1p2~w%jKu@g1^eS3s&}%fd zs&tNMh)1|;Nn8-!hCa5^sq&Q_kpyC`3x$2&6(;`rDRzz{o?W5vs1G#>hc4Bx`tmBt!l-P>Q zQL#8)%37r`dz5@s=crh8E@cn8FtOVRxlyr>TdsY$NN0?S1=4c;H0{K@s91L_SLAkz zSX36zkm>{ z4yCNn3KOnng(g<@N!b}GT&WaKu~;%EWoM*t0b58G3(};_M+#foV?iy}ddXi!g^EQ~ zQiv`xr4K3=C>0;|3@TRsNEug(P?Um-)ihE9rO4DCs92;TWxiUZ5(-qT{*Z#aNOcLQ zSW+P+!i(^@9~J8qq|CC5_yiplAO9DtBWYB8qArE`B0i%=#Yfbre*3(}TUy?B-bWw0 z@;tQSL?r*Z^qXD#`}+oug8I9C14zF>lQF=x@2JtPOu>0)xCO$Mt!(THa|_o1qpy(+ zG$Qk$P_7^_=caZFyW#ulg4B#;W$uLx1X*BZZl0njQ1Kikh4q+w;A>vPk6kojg?tERNW93Pf z%RhgsX+x#0%2r`FFGyy+(vz#&P$?2OD(r^04A$*fQWf@Le0^w931fxbf&a}2CLs5{ ztb@eCdx@(`E9~?Q(^&htE0>gFqn#^0DUc>UEc`u^ZT!zU4s5uKDpe}%Dpe-3_H&aO zvai5bWD{-uDeU(Ca^^5UY2u&l*ihiT?DoVJcAMfUnGCwK;WrTWHx(Rh3brWh_M}hD zW}mhv+>!mZcrQ~v1-BG-hc$o1Fi*_b+TZ&9M8{JKyYs0~)+;o*lQm4+^A&~N0ZaUP zmiqKtC9GMo+tgFo?dqMK%e>#F#|@C>5D@(9<+ClNu-li)jb%NT&qb^s39h39{0)}; zQ-$5Z;i1_qEsMfSAPJUd+H6!S>~^Z>vA=rg)FTM8(BfoWE=pl{fU}n+Wm8f&>vzj% zyHH`bUl`B5TUJNwUrw}fq_EpLIx&x7B=@7c_4D#MJW$x39xRSwJ*RcCex7K9O<}jg zwvv6`zMCrRAHZ%4T4A>box=X+8FN(ZH_g@M7QSh*j3s#n^JC3D7QlYeoUPReCRXS? zWBZFuD|#Za@GF72rK~dCSRyK`ym}_nc4*9j5^Qh8TU)g*mV1PA9mcT;L_ls%$5o)> zjj>1b8BWhWeS-+l!Pu&C@wU{`IL1CB5*46ZvM3d(cuVLNXMArJB0y)Xty&lF$HcL- zhE4ashWj?qpqi)0Hd}dpM(#QPUKb3*nF|)kp-MupS5Zok58|Fy*~*^zYcj`r`_cYm z1IOtIXF~1C|I3sACwFgvCp&@QhC7QS)7ST*gXl&f{(1vX?s@~u!vmdFp0ttDYfKt~ zQW+qfN>7`VgpQ_^I>KPmYe99$rXEK4R|z7q1`5Dt_f+6Gz+-*aqIB1eqFPrFs#K$J#L^V z9Z5oBpvGi?v^A4a4OQhaLPcwI`7vysO??v2W^b15hP`@i8aHZNsUN2JXLAQc5=~9^27!8=xh*26nMd_h50}dJQCb%XAb}k@alT?YB zw@rC3NMH*i7zFi!>fa4}7V#?NcI9>_+IrNkEKj5ir{!+do*6N5)r>=n&i_1U#1Z70 z964nNG@$jbV9!4?W4=l~gUsYaol*K$9j&yV4s>Hqpw@SUxFYxrq^Q8}?6k*PBnCbx-;=Z)DiV z_fusImtptU8k3{;y=uQLGiHD~w#&pb8M0+p2;P{IHUC`flIl`E&B?87V&n{YBW5@* zKY2|0+4fIHdnK*;D}Lyfd6GUim^GMay;cP``oNq`Lt&T_rWdrDg!&Co$APkm;QX$k zC`gwpYy?33ZtXm7TCz*5+rZ4+KGAtyr^^;B!|a^YrVlzaso&&8oz{=TYBf>ZloGv# z$gB{%A#=#;b8mNb?2s2VZvZibmNQ<4U$@TRgsfiG-D~aov`%wd8EHwuK%4X?N~gvs z6J%ZLRBEkS52Z(xxP~V5xDr#^L~eeUQxqg}g$-MX+?(@!Q%?^3Yh9cEyWF1rvCJS_ zune1Wp~jr}!k(ZN}nQhEk8T=7E^6h&){G>zl1UM36#L7Q;si3zyE$|iETO*VGY zAY55dkig~l=GgsFwlpt9aE`~O-!4!suK3Su{9(+33ApS*Gfd~jWrn6+cRq0-B%bt0mOEyK=PnHnjC@Dk4*BbO{gjEBl@*e9stU3H zupC_oY{y^8&24q?hw?M`8X64ye$lewKlLP zZqM|{qZ7j%)8`&=l{;oOqEChVvvuQpFVjE6{ktCOzE-Y-41cmHP~G*f)v-g~`_;Ml zL$`p$5oL_U05-$6ck>2sXHS{db$+ZSev)$+p9eA>8_nmj0^hf{Hh8$>V@7>VwF*-o zo~c}?SO8}j4=eY)Y|z@&BiF_z)hm;;^qpJ-g+Im?_}>2UezkI^E(QG2$no)<4xT5A z0c?itHY4UP`T5oAN4;w$=)&)Kosw%0Xg>RgCC#BKBTuEeZroePkhHeTp&SQEyLE*) zj~eAG1jo+X32~R7ttjms`CTKIve`G}jhKmxIqF8C?UtUocX#sJ*G<0Z^+B%Phgqv3 zP(3zj!IrqJpkAxP_q@ky4a_bUz!}CzUPwq>J@ef3m0LqReHXQFCD%zGt8o$7zJYBR zzU8c|%dac4cV-L+-cfS1f=tjOWc-o?54R$i}`qv)j#T(k5E2VZth-1hz9$FCEHQw|4s!_oJuF z5>sMi51L_m%eGAn?CX;a0y&gH8`z5~jD2Em($G@9HB%H}u>(@yE@3*8ULt z{(`i#fx=@}ILq)_RNqKn#fErz5dF8V>-W2y@iP4W{vmbZ;M$RO2ln)A6}M$yZ%L8@ zP#|Jzs4q(@Ni9LBa0-+=29-*W(Qr)#)b+JF%YewWzow!fry0B>tBtBRY30?ZQsG`N zMpupfY1U|5#&8*S!<>dC+^*BGUZnDrSIM-oB_z%F3zc0Vek-(38+!HTfY%qdckbrb zC}xbj5i=Y+`cBGzHD7h>Wk}VMou(~Fmn2T4^pu8BL+xoLMqmW4QG>8nqta2h9`3GC zY6+VN77~P{_r8KB8^fCy8Hc$OV>8mnPapqHYN;$)17;Y0+j&~YMI+wO?VD7L=~b(C zyrjK>LS|Tq<9C`SEuIc}(QSR#J+105oaiZc%nZ-bwcOVP-`)0;&zLJyt5wRqA_=nw z)9BS0O;b?j8&iWg0tzL=l!DL@S`yP4Y-bUoD=iAr3+I<&29pVL;`%DpUZ|Q+y5{V> zU?X-=(o%#Vp$fbjG~!T<9I$ItAZS*SdYJT>aNJ1QVz#>}LMm#j1UCS_G_~X1ntxT_ zc>{0W!*FW-k9VbP=xPbQN{zz>H>6QX(3A<&5IRb$gzCL&<_;WN;GoJE61#-by#>$r5Vk`8$68*r&|7 z8nKV0#5Z)cE9{2!Y|blsD6&AvZ?o=l6sWk*nLeBQT^x~k!NI0$RiNU& zHI(z1>4+qlJ*-pIeSH;n-&hRi6J45re;DJfv(p(DaWY#R=aO#gV$LyVT*SRlB>$9h zNSuJo#$G%szv9SxUCKh1^~^(oil-iVoI^%Q3t2YfrveoZpzrwKqa?DxJ_((92XW@p z)HkP?4=TCDpBmTaS9`Sg3vGI0*1 zndYPaRM-hX)41<2xv9N9`v9@_@-qM3evicS?c1sz6{uMDn8kS{{EFn~!ahVSIeg7O z1m0r4@Dz*MPl1Z%eo-@7#?M}`w`bcIOYM?5`^4Rl+0VCcr*eam5+a$~I?ijI@;{u| zM~M|?e{oJ3z0LeuYr7PvSlo4tvrlMZZ_lWP5+yBALl$D}Mfr=$fxt#f551`@# z|K@@{2VW#7nm3DOHe)q(hP=J4l1G7xr96u{mlsv@^I#t$R?)2FJQCX?3H&p}`V|E# z7O<@2d{TbQ--msUSUa+ve~!tF%vQCbK*h3+lbpw2+Z^(9VILxvOg!LRGVWSp(MqkL zu#+o1;p~t3AhG<*n}q@jRIC(u#TlnNiHun!;zRm>c_dxT6p4?=6{z@pJR*|)y`x*? z@54Svd^A0W^GW>C9J1Dj!U|M;DxApKXJGdBZ2RH^*%ZG0u4YW)qfn?=AUX@(#J@+Y z{C(Kxh>s!n^SCH8F6#q81u8xPOyle~Sw2;>J=?zc@a;U`es6Ou*&cf-Q1Q9fRnB8w zv-~{RXNZrS?r6kYvL5sJ^VoW!U2ilnlsP* z#0j2cgNHjDmq%|t+@){pk+4C*_HPR<^2oImVrHe;I;pII&7)&&_i zORy9PwI!IJM|@ngm@_@Z(au}4jfxLy)^nDLy;+X90u)S(;uDhXoZYeZ7CW{*@zKP6 z9^|wVEI;NN6-=ArLjm4)se_Fv+pKsu{t4e~ZzyK5DdFAxRpf6scs0GKMI#WGtcxndc!xNSO&yk_uNe8XBd{38_$& zWUi!1-o5v;_j7;0bI!Y#|61>Q*Sp?juXWG4>vO-KXWGwxrgM&~P9h0%&EXg6-;4j> ze_i;ug%((7frS=WXn}4b7^`19@R(*eNUOkn(qO^VOf zmA37b^hIy$^ZO&7ADfJ$@J`bC(l5eE(rfvR`L1h_}88`TgrWr`U&u z08dy?@QA8ktV+WcZGeZpB6zfbyEU2hYh zvnL3C%3konzNf4Q0l#cU@EEqYi+08-e+E2j7v!yV(%;`X;q9;OMDSR-VA;>-n!kZ~ zkHwH{ZMJ?aRDs{ll;@D2d*ks+pgSMLn`#g|PUQ2^MNa0CfE(3AF5?zlYcuo_+;{%rD>FB1L0q*-2^48U*(?7dSF;3=!?Q_~`iKh0P72a+JUPS!q z{J}G8RUVmxbaZAQZyqY{y>z<-@HA=2TV5J5E!!Fac)TLP6Vlm|M_!(;1pGoEYsIo!KDYp2&Pon2!N)hg!(H?-wqwK2s0)89m5<&{x;(RtM>jIVT97yeDe8UyGn1 z7Ek7e@yRi@V=-2Z7eP9@F#WS!xmCeQ>ns4*IYgv$b}wD8Y3$kafM0Te++f>1dG=Mx zfJdf4Zv5wJlj7_Hz%S@PevR&=qO2DFTpZ#g__-WEPPeD@wIKdDj8AdA6``G1>IArH zF%h52(HW=daiJM-uW5p(#dr40x~WU!=?Fo7^+og53Gt(VAGITRx|2w2@wg2K;Hi%x z_wD#b9u~yYH-z^k{h@5dAr*%>5U;fz#&^6vlPpxV25=Vzf@dtZ7+>J3CLxR zWpLcTw*qjRJ&-G24p3cVwj1yO`21!l2gR#3`NRO8=?~*Awq-{zW>o=P3--^9lxT(~ z!EM_CkCr6jv$na$1no;70^Btba;0yJH(c6~3b?)&a>YkF2CGjC2P4}NC9}sWeLd559@hv$c7b69D zEPO8VtKP>+IQ8N4m+^fV-^yN|BM=-0;w|9wdqK57y7h3vFThXa5b+lxy2SLj?0#UJ z%o#`Ug1ybV?rry)0r5vSLf)_YW)t%ge4Nb5B6#6C?d`3LzOsRM_mhzCtMk0_$=?|8 z!o`qRd9`0QJTihXTtA%6pgh9@t4R1FSZeTM$+`d$J?AEkOvOt zeDyqt_kTNj$WJMWvZn-gf^;l?6a32i-EzjCGgbk9s*>O(MV3omyx_P8xB*;0Tw}2v zd)M0U0eG$z5r0i7rLk{T;~L=BMv%9>D%BBjz{jy;;gJ8>v*(QJ>qZcNq=?|xjoiM( zY0bU@+@uuphcS#jdwnJWw`C!CDKC3{-wGyt-DUU!av_<0>;c?(e@=w`vo!j=%Bnsm zVUW%_m`<6sn#Zn<;~{`YeI?QU-%d2Oz$Advnkn0{sSx|4f&2etw337^Nx8jaG^ zlA8E)aU8Z!<&&>%JZqWoc_b;4$hUIPUcj97xe-XmCm8a-PqwCr8sY1leeij#O7^;v zo*BV{mj~XjstTV=+RH930z8YGNT=%Oa^HvPvo`>Df$7`~E7jdpsfn)xQ|O8K+seyA z+Gky-K>S5T$e&7{Fuf~+>=DpQdlvF;*B{TWv*GC*<`evmZ=SjF+nhoWp8&`CJFiwd z`X?#00B)O0#NX9w&b~eqw+->BIO70=LIG80P@@-9zwu`fJ!t zrz=_k&xZZ2dY$R7{h5*Yyb#()#8;n5+4e|OsT{=XjX>VM&1fQWCH}nc4Tbz0n_R~J ztN8e7oi$U;B9ESpS{dczr|Qd|ju-n&*7+FrKd-91rVydQv#E2Gc?MM__x_efqKa zG1q#0JoGw9qlsaO3gOF_4#KesCCZ9t7!M8GyX}!%Oiy z<;wvN+XeYU`@Zsoh91BVz~|_GS7gN4IqeR>4T^~P2Z#9w97M0k0GN|)(U`X)#osk@7M$Y*C+6TwC`muFZbV1B` zeC9P6|1*ASU1oeDNGA>U&!-1fQ;t=h{|30uVUkfh9p0TELHuRd9~uv;WtHxr zs{%Y7J}-^sZYNZ_&*JIl!G7MvNg|b8?E%jtDG;_#vxKv-#A{7AkdFN!qC73%>E3wl zRmSJFFfW3)ZWTE8C;#{ah%bQmy`Ae-a?Ca#e4pzN=eu@~cU4Q}eiwjvQ(hvS_9*+8 z2bkLF05^mEvwcY3Sa{}aFyJxoi1-&yj~LSaCg41?5Axi;RZCw4egg4scOlOk<$O?S zrw(}X9)fpN&X_&@%pVQ7Gh8oqbgFVLVU8;TJm5JI|8l3DQ1@jo^r#8Bf}bN7RP6gwH(LHvnD1n(ODy^{Rwr7_@p;rRb*bJ*_e?vfRNhZPa=J#SKM zJ~*w#$0f@%1b^+_t!J6rXb0l+;k@~#`RUJU^JH1TEe{g$Z{|L&H&vefjB)bfG=je+ zTMRb%vZaA|mm`qN$TP4fN#H!l0&@KZoydBjCJ>(m`@`EKm!xD2^tAwwS_|VpD#t&G z3)2NWV3y#$t`AT0?J&Uizrhla$C@8^5xs^#zZoAOPf9<2L@ojB%Sea)A z57Kvs{i?TnuaV-uV7#3BV7>Z;Jmu`kJ6b@z|8^psKFNY3oWkFq0`36o+gGr)X29Om z0q__dBEFy3Gp2U%-7Mf4a6avKKGy3~vW^V+2^S*%-6ysTb*UD7ezb%A>OGyg#_ruk zCLlfnj(hJH{iz;2SZxdVVK{EQk9eS*+@81t@FQ?O8fZAQ(n57A5paD0BHw{-B^J{U zb{7B-g5%Y|pR{zLa0^kukHL9-kUrJ;PM^UEzK=S;J)ONE-W!f@AGD`uM*O$01pI6ljJGSuVqiTS z3wZHO$op5frO@lW1>6fh7at|)LVHfXKL9vv{<)9x5?A*%s^R%st|!v@_{v8t;KF%% z5FZQ2v7wVJ`(u}f&Mjd8^Q$W0D1Hnf{u5MRudWY{jv{(o}wrsldnw`#N z5Fc#<`Jp|AhF5wr0k?zC^_ai&nE!+TzQ1{Ct~b#nO(*K_aqa1%2G&@S!_Rac-4qu)tzJ3eH zAmYEB{2KP+{S+sN&rKxw^lG8H*5hGy7$oL+5J%Y1refF`4lWzpXAAsW?%cI0wS`K^N0r$@(;#rTEDz_=K;rr-= zFdf#3_|Sl+IDFi@1nb4N({)sjG!E9)q;qXVI&9J2YD?RkmV)%p!|{;qPgK>bUQZFg z^X?My>>Q@#?fdfg0PYR%BRjW;n#{xTe!%tMyveRuS?Crv3;G+$#+OKEvB^`fnRNDC z5N`_a%i^mep|y6u@cqw`l;y!Qmbxff|z{}Y?g0=Rx3!Fkqv;g=4a!}nQcFCo7ec=xx> zTmy&?hvNyaS8=S5dBRJ;V@ip5-n^2bkjb}rdmecO`QwUp)22iCdfuY|^8UoX^yFl` zJSk3)zpFXdT+B=l(l=KmIG=*mC6cf_K0fSgg8VFdWNQD4LJ%LPOmP0ULgUwdd*ka0 zul0~mv0qF4`0zA{H-hcFWccl*qhbL*Uu41Z3y9}F$$1qw0pd*piFAY%Ro3ciFv$R( zk^%WQj;hB^g7|*00N$^q>uT@YFPq5+@kIxTcww34N0mA*;OiWdcaR_R(9V)O%nag# zZxdYP{;^+GB7**apJ*ZYGOeOl2CAuF0C!=8+=hAAiK^NAfSbYgU(R$^DR*Q8zCN)j zA>x;_S?rR}YZ3?XCvQTow?zJ-M}0rwp#qTKVph?v;MoHBeiO*YkBx-r)r#Pv z*Bm3oBvPyaj~*kq*yrgEixs1I`|tfg@D;*3Z<;)PKY@5R7{4NFm)|1aCkcQbhUJmq zKWW0Hp@X+WoI8<@gnOz&geoIGZ|;ZB`%1M>8dp~zeg@KUT1Lc6TD6pwWyaw5wd2 za+TcbbhUu%oF%w)<1}-Sef?HEog$Uoh>vYz|%MUYN58^Jexv#cqxE0YAgIFjHSh3$3^ z6mGr;_-Q%F#kop;M+>e2Jgt`Cn=E&fU;JxP54eXimD^Kcz-*e0^==RSj}#kX@htR zSbjOLQJ>tL)!BgCZ-DW)1)Z87t6KpcR7>#9EMKygdOukWxSuNI8eTOAqZqydUa|*r z>-NM!JzgWgPbfm}mnu5+)yW?4h%AC{;SZX=Eb09O@PcB9Lgd~uD9_;@+49EuY!(KIj=dLB8GYi}(AM z%!(B&0YA%0q@&K$&k@c(g|C+)>mipq`rEDUEB%mDVy-mb-wb!tqW0{*4}C zPa(Yj7o3FgzlZ83^@%soow`T=oR`?LtHoz~z=V*^>`jM%+Tzs97@Q@HR6@Tcx ze$f_(PjI6*;mY#we<6Dm^zww`hOv<#YfWV2bHMk)dD+;#_4^J}@g~6YVZDr7I2XI5 zos|OoN;#3f@weSo3>uaAxDf)=+0U9ZKBcn}pGRE367l;#8D0KI&ha)#rx31}4oKaT z{3x(A5AcgQMErr8g7XZA;_>IMP#N-iznuJsPhW%hBG?a2gcSLoxZKMDJY|-MH+gE= zCEgaN2zW9a_e^?1*(5(IgafVz@4Km#o1^QG^hCh5+K6;aEkmwL+#lHlxZg#{Uv5cN zZ52NTc%eJw{Ss-so3@z(ZaGbGGYPTid-?D1dCe|^;1@mk2Y8de@C`4I);&u=8S)nfCyW+qO2U3VGw6RV_KDF=-L@#pJM7mWYV7^&sp zhPQLFFTt((uQN+BUS$XApM~|c7EJZ=_AADpi^7*M-b!N4Uh$&_ApR&EudLs-#3$Zx zm;l`G1QBn;&T_a-{ z@S`a({{5rF&r&B(0Ui(gl`Zqbn^!hx1_Q2@OT^nsD=FmB8TkMn2kT|)Cvh<;L-#S@ zXW;m6Tj(C2G9kqZ`1zkOop17S>t>_y^IlC@U)$-45dO|FIS`*wLB!kjvMH)hH{t!t z1+HW5Nqy%}J*tz>~xwAI=c$6mu~GJcp6shff-v?!564-^V4FK>o~TV^Or}EQt4o z{qx9`?dJ@QpY{NrE{7EAs-o;xo;YX(#K92dq zd|f7H-$trC;rqMnV?_K>v-Qf%&O`X~7@`k(BRP=0{Ug4ZI|c1}#SX29p)#0eNb z&Uv70pcG%(h)CyH>|WJeNqan~yHKPsu11HSKHo_$jic!q3E}HoFWBGwcepI`iT~#dh(A+K#QUpW z)gQ_Dj6bI_LXi6?arNDM`~t+=pM^XsoWaDm`6J*L{*5nw8<*4^hmZ67;dAHzi~G32 zv#NFwpZu0cCm`nTKyxD}z8*Lw3VE#hZB4QJc>gqn&wIcH19#?zxe$m)9fIu~O!}O? zW!-o;h(7?!Nu-DT9)Md8`H#}bKjP>=T$^5sm!`nHs=g_stv=rXG;dexlwK{5agRxx3>JPPHX0lq_i+`MxwU=w9>?$c_ zR6va}F)WYmmzAlF2N~C#9a}6oV}`g2XEI;30iy@BvxcrLwehuo`$(GbYetP0t8(2E zA4@!s1T2q@%wp8WF9t6)<@_Bjw!-@oQx-eD14h?dU|6J48?r@ZwFz4fV1hb>Hnp z7vxuz7Nrfb^8lke3pGg0(&$J+aX;8keO@QR{`0=bp2F>Y-LH_b54#^-m|@xxLT!+B zGT0AQ8ARJ|c6?07x(m6zUrFsXRAf|e>1t^RqueKgi(=(rDSRKP>`Dp-o22o@5 zGFB>jJ9N!QVRRT;@aBsI@Jh#r4hP*gJ_?K;FglR0_EQ_Yk(ULs`F6DMwre$8s|X=| z7Hca;EDzE-^iERbA$Drz2LDy1;vw6uIxd{c%mGFhzH--%qc)EDnK3prsxEshDPmfw zCR~ZAfR=P^Y|ESAv-*F#akjEt0wbM1BpCV9{>L1IQ zy1Lsuvd)D!R-4u99{@(@E;I|0zzTM1qhaogisHsKvb-WQ_ja%exB%lNe$SHfqn+gphaAAjsdW_zsfUdIgb zu@ltBYMoO81C_oNJ56O=oYyGs1;!-W%aMO|L+4wHJQ^00cuS6}ajK;cf06m2Hi{b@ zn8CE!hT5?4$@$Qwv(|FBdzHasQKj#|nD~VmWOp`dLygYJxqQO-;r*cH?MZtY8gXM1 zGnf?77N98AkJ+At^#V6tV*~EQCJP)#o>sKx6JS0huR_;k&fFT(H47wC*!)RKlQTZ#sp|N+*l<%Oe8!%SY1B0}M+Te=4zi8+}!@5y_ zvR-l|`l%*bDmn0+Yft>9Hu5a&9?Oi~`uI=&M`icsUsx}hJfwLO%R>NpB&piUHT~DK zc7J2_U4gyN{4>IELkm2E{piF=*}Bb{&NA_RtFH=QxmWD*K35);N^3V7N+KbhhRTR< z5Q{H&Tq(ET+J9|6$#)z#Y|&7Xm;w4qaR&!&GxjG$>2)H%Lu;O`Y00?Jk0wrXrIm+>=E?&vH!kPgTW}~jSjU7W!80L4@&5g-S>UJ&Xnc0?XCJzjijhJyi34Nn5%J+Q=yKpY%r%u8H zhnj?+$T)@Fb2H2!KcHdQTv1rNfA?jh9CJpy$3lWCxZ#Bv613WC)TpfJh3%TIW63LK zy;J-)0YeoTf02uY2Ysa|)zrCxklHuvpFQXwa}r$s8c7>1m8t_8O5!?>zET+E%5vit zcfP_J>z(~3)^4wH;{qB=TCIz|QW*C*^8$aTcZ$WO&PG){%R^2xusrI}P?E$X`buH6 zoJecw^kmLEx#$=}UZYVVFw~IsB64j;x-ylqTl9hRZ`*eP_r5N_>_33k6wN~obaxqA zZI#+JIl1kj4*RJZJJExxt`@if#zgi5H1co|WQj^OsBjZm-{C1_{25y_AbFfcLrHwJ z+KOXJ*kvQT{%1oMsr_A|>c~8fmFh8OFw+`K)DHAAzfi9=zdRW2Dz!=SDsCXHj9fgl z+REaEmsyf-cWk|XD1CR*gAKsgwGA`OdeJwE)^%tKn7L8<$J%km>DZn}S|zyQff+~2 zXc&9$T{>g&$4$6HN7EzkFf-CS(0c4b?hA4;Vp&n-ab&gVuhYR1d(NKOk{Y;vH?}T8 zj9Zw&X@I^_81HU~e!4l=zkCb-;_y!`Ji>S$Q<%YvO>PuMDBD*S&yw0FuTK^%V;;@^ z4GeW=%n(YVkw?|sM}D@ZmmdF!(~l*pSRyw7t%o|YJ3=l!YZ}Im!%0~--!FYO6iVM3 z_TIV)H$pK(QI>}Bh}XHPc0*~6uw6mP?nk3^xPh)gNu;HNG>qHt($95%$z60LcAdBk z%Lo#QmP!Mi$C0OYl!lSGSv+KCcFTvspkF^ER_P-v7tC0X8Qiq);K_oNigUxB`j^70 z|HulNj^GBe+CVPWDD;)0b)8?N=UqD07J1=DfaTE)W#lGfd7MN;NxZb~;9J@2P8F>i zWb$)IZV$&kYrqYp4lt+x7nb2~?N?BM2uH_}R&Q$8Y(EtUs17?QYKQIDcjj)j|( zRXoLZg|+`sviP`TKQQpgnS)k6ree<53NHyLHNHKvGe%-ZJZ>yQ6DJAMYODU*myi0d zmQ)2B)Hk!p2Pfc$K4vg?qi+~_Konj^!@o!=kj z44+x|_=R$XlKS{X+{nQU7Fwgvx6MY+B91kASM#Uot$uS0Uo-5k#Ehl1+Ui4JMO);m z3#5x12N$LFd|U(akO$8>^CeWIsE6m=%ujXZ_E%zaSMt=Y;(T!<6U)QuI1QsLZ_gd| zb%M9dqkjrn-v5fM_RyNkKfw(8kF*TuvDj@B?^UJS)t6Xxoe;+jWS@>)ijy>qRbQTN z&-=SIYeUPjo?ZD*@aIDTtV@Je&@dcziW0W!FHg_1>^$K9BL;6>B@r}~JC0UcO~11i zS8Gfc>?rx>T4bAztQF9D>;(6mDVatdH&5s`ZQJmw|x1Glc%;CH^3cCm!)AW z+myq(jFV5-iAp`_|5TY=g+lQJ&|$_=y*?ZdbvK zP+EDU>)yV{zSHk*pOQwX-8+6;kcZ}B%pf6;Bvq;ISpPC-;iG@CBmdj>#i9zxo&YVC zCK##tl4uxhj2gHen+4~HRRQ`siYjy|6$Cbpy+ zHN=Q&s3@BO1{>PlDcb6ME4};c_xmKK!p{ePA$jjZjq4Lv=72%JirV;}pGJ+1|6Fx6 zls8Y(k#s2Mc0ICIAV<3jb^3!m1Y4=|FpD|0GGTPCF39vKrjHE)Y7 z-a0!yz3WkA#&fy|=cgzVtT{;%lEPY6DaRtajLki2| zfHSf-rOcx|!{MNT{K@`q-$>t8wM@gPq4_luJhcK*s7O(&Um6j+G7kT=SC-&R&=*>o z3XG`TSRVbfp4#7AB-Tt=eAB$Pr+VA+G=EFn(7_DaduJhoVz0r4Y2C^2VhR%NuTx;mMduHsQ~?{%P?7|)6rwUTg9eHNl*y7F zALed2vAFHQ4KS9>(7NY0G<+foWzMLX4ovQgpV|`%jBo)oaT1Rh`btr%#M+fZDiX^j zo(XRs3l)+;0m}6Lraq0?V(qhic2<;w?L*!%y33SN#sW~3^k2a0hcan zS(jXUuAa2bfiSRJN1E^68NGwgu{`FxcSem)%$V=q88wh+5xM5OcSemLm@(hIGh#%r zVa9y-&Zw~xGv>Q@Mh)!YBh7d3j2b(zJZP?vY3!X*LlZORyLUzntkceW?~EE)7bMMh z?~EGQ0+BS|y)$Yct3TwL@7@_TE?~xd_s*zs6*K0$cSemW%$V=q88sR)W4?Q5)Ie64 z$Ti=+Gir=u#(ekAsPP>$=DT-BjlY;N-@P+pL^5HIx_^CQfl|BSv-7(y9`5>6SJNdI*Td8jFry%#s$ zU-bLfoLhTicnS_S!2&Y^REkktj2$0y7X+DRjie1V|2y6?)B0|_RZ&+PVZ0Y)fR z9PZ>+8b$)6;H^xVS{e4|Qfe%DvC+5zPVQ*#o!NuDyIYmVk6RZM-%GWsKt@k&KIcFa zr@eRPT%rEHm)ZMm)!C!E3YomIrHZQZls{0 zq_i>`hIPhr2m2mb3nL@>12?Yjzk(ZJq$Xj*FU9@1GF$6!zBE?ERM&Fbxr#bN+_;A2 zL38iCEbu~498;L}x8rFQ{@EpIi0xn*8Xf($I7hPw@UPgXZ3u{%GLH_p@?+(*b;p>Jce;PmBcPF3s85F218) z=Z#)&%5{yB&nvy{(L6Rs7=Ss7#DTT|MO$4^;$7w=KIgMB(k!T}DCH<>AiM2&tR5sX z^1M(QmXEUf)muHKY9{V``&~WWh8sw4K`*j4@3Nj~v(5ATJa~ht8$p{MY3Ux~k%PJ}L3#p@xw1C$JwRG4@d#f>~u* z9$$}7bV!CZX8TJcyE@F+h(sb6`#I!=B9B8-ub;L5;O>??oprA+E)bb9$}c)0^FJ138zY%wx^+nAHq&CYn~?ic|brmm}?u z8D79(Xr?yi>}Jic1P&dioQ_f5fgS&tC3z_x$KRgrCHh8+qe;ehLT9wq(EWZZ2Ne|*J7*YCMo^k zq<}p1^AEHhpeK@_pc;i?7@(cQvF@MUKEhiRx|N?l#|`veBmatYOe(|gG#?|w=JjW9 z+RW`^ZWB0z8?k68iH=qtDtFml+VHH*GHY34X}j=oYDf=A=_Im*oC3UYr=rQ09riW^{;N~U!`ybU!I5A2W+ z5ZI!-Z>Z3*6ETo)a&#-8iIWWV(enn1Jh~LuIqmn*xb#$MCMchMx&RmhuTX)ZsH~FO z_>y0DqFV2E+2_BLHXq68A_2{#M+-Gb5v(*uIP>S@rM)LoMGEOvTsJC8ssp1dA2ZBP z`XTQWrPAA!v^(lT#gVanE?FE!eaL-4zlHSbI%fDjeo1ZcKUPiU_uH*++Edlf=tHIQq8T)2yR~!lODeyuM~o;5u~*zcpnFek z{JegXzIx98j+DSx#fs-7WDLQKCAcv_ZG3pNwLre}eSyWAe50WV8>Ce+V<~P7QX6_B zf~|S1TbLZ~|JeA=B#RC;N?E|i7)UhI&nW8=;r8^B`nNAPH#YT0Z8T!Uo)6@d^AgAd zdq5~^zQiTw`$)*h%>il8&!^Vk3`X*ZmW*S?Pn}7{)Oo1bNnF}g*_JN0&!)cRq6;z> zBBf$32X~M}mZ3HV_{UBN*$?)8j7yYaFKV^}c}OEq7J88bkQa(l<*L6;`FQk*Y)X&z zqjN&z8mNKHQO_`eL@uW`c(2`fJ0aY+PjiXo)8;Y9H)yFcEpzaDPDV>dk;fLjU289v zGpKdF{=L(7Qzp`%kX43q8Qxa(P1HtG;P!*cb~--Jms$m{DW)M2!}eS9NZulJ6pbp> zSWyM?pua|K#Bv$?de&kuOZg+QWDJLhdy>`n(K71=2>-G31rPP_^kER7pDZ<7pe4zp1Y)vI9nq zj+2aGf<=Ji`z28S#Q`r_B#M{0~3-5r>rk$ZqT51p+kxxoqT8i9B*by z8~kiHwr@160T`e4QG>LRmBwnx@8FL|ziK2l%jx`_eaXX91B}ir%wS%#0(qy%V?^ei zfq+7^+UK?imQuAc^!Y&Q(TTJ&a+!`HD;r8Rum2dLC-@+hPV& zJ!(?q;r?9U?A`ogK`E1{hVc`3k(DvlRyLSH?;=8NoF<1%au+jbeyiA^^LOAFcfm3*qv6q3x;tw? z9v$z1LF-$^qpYdzOZ*tywmtb}7C30u2#ii-RzNPYBsw2b)O^cJ{R1x_D_Oth891_> zo(-7=u$s?c2F-634@l`QzO3$Kdm(LUdh&=7KG%N6*P^t(RUCG^F(*#h^QU8tisyiA zZzNuidms<&@5oS;>au7}(`<45Shv?sF9p`?Pl3_jh8g5zM%489U;D_KfsF$3^^=($Qy-GU2Ck_ zv1LE&?LS{1f7!K52jtO>%p=GZF_T1XeAmq@KbAjPx;w+@K(~}%8Zch2#0>MG5^Ce0 zY98yBh)!eHGnTH3MO%=S3wA$FW5z)PbbUfmsy7N_Q%PkC=8L^qlD-((BW;8lpO<6C z?v-e-r!YQBeU%@a(_6c#wZ)n~k^$XKAV!B6W^CiGqc+NRo#9cs_ep?#DD-!&#-eG& zh?a|*`hXcEre)N|(K1yogXBMvzwJ6temSUu)f}16#el&`W3F9$?~HCu=B>g|E}c&r zAA5hId2CSjL)TU4<&FG9Q4hYm4_pfN_ng16-28-B&M!%{9!MU@?1o+FeW5Ul`-<8< zguU3*`19%ST3VPu zA{$a0iw4bpUST@$dhYaopYpoUcGN)D40X7HtU4(3$QJ9Cdge2*>dv(jDN)prj9a4t3_7GSl+)a zvii{1=^;z)kWFYF8@ARY0)r8KqiCzykeN0{tEBYP5^wpp{c{3Y*(0SgzbOU`1{!&M zzqGMLTp)YJPgB*u)xY95f;?K0wKaM%^dm16d5jc|6)k%G?w9Hso55bDO&qupfW{+V zh+B)iP#Esl?ydQGLGooEqv*Mmu{s;nP&%oF&p1pW)JB-9CyTpCsLSIu2R7{(eRBZM z1Hb1iTc{0@5!)sa6T5+BHMaj)D=1?313CAJMk0}mfyPr?oH_HR$Mv)F*^S#@Ou9EX zB6&m&(5VwW@hzfJ)Z^ZyTUYuvo(rq3wdcmg1d$abIqJRMXMFx3Bl9Aq5&JIZmUm^c zw^SH&;FC`A9hhOCt%sHgIZNG!yinx9tA0H=skNMIDw<`=%%Bw=8KQSOoKnC95~BpQ zv22~}txp_3(hR>Ie$9C8&tBBnz~B1?7!1gMgffpzC1yX*nFZ&i(=2J)OA z``_o$6C-eAWbC8FMAD;`=_W?#D2kQ}e?I7G%pbS9CbbFcFPlIJ{F= zlrdHO))4yeSQjmahy2)!oqPJ@z`d5wbIt+~c&A?DZ>P4AH-90IhrDM*I!hf2JZgV=l0vP7V$_%-< zTF|^GO65M#ux9Ro)0Xp{*BiSHvh{&+-~ncEuR-=BltyAk?Ne4OI}@k7+7F+<{(2Ix z2e{`eFCcRkr9nDx&v#^B?)9Y-e&6r3>-yt)gkX8Bl9`}3#uVfqMh|oM6-gJYo7i`QcuGstH4)W$kd_Mds`o9itGnU@(pi@SxF3NevO(eenjLHoOdFAOk)#3)8> z&|X>2)B%Im%98fC4R6^1WB%VZEWr(0-!`QEIgXJS$b;7BIJ7@w`3wY_pRv$h$+&@g zPMZHpMn4Z^GXIs#!!lsZe9PMw)I-zMJ7yTmS zg(45y-;{lW#UnokXr(r2pC^`!0fp9iBJJH>mD66!oCJAB09Q zwL$ySSK^-2`t+6d=bVH=>vK-ppVtxwtJF?_6m7Y42eXp|G^3wz1PS^ zYlY0>(P+?gRP?sN0mW{eAWwWY*x!i>ObyA%JH`FrQNP=#G-xb4njawF0tE$L7JKm_hSYjXU19c*QVVTqo_Aj?Erztx}V z-rV-fbY;>@#~n@eH-Bm3d01iw`Pd=kouVEzzC#}?b!I10I`pFT9eNtyh(`^)=Cr;M zPvcwn|6$Pj);*2y(Eo=q&+pKq#tl#pTHm3ku|h@+WpvewT(nlmG|rY#1L-a3MeA&d z#&;%AqYxPL{LTbwfb}lT?@Z9x38O|M$YY*6VblO418JT+VZ=}Y&pFNAHjSO|e;D)J z38O{`sK-2a!l)4ojCt;aQR5si=D8C_jq|{m=T7)PjCt;aQR6bmgVs)%#%HOBLG*T7 zpQX|`vqTJ{@6tN6r17m6)VK~xMMn3Y6k`dEZ@v77G0$(kpawEmp%=|>y-R5X5xgc@srG0$Hjp$2{jY5fw3#;3@r zfy{2`Me9>!%ZZCJOP#tN=RCN;QB~oeht7jiXLc>e1k&+d^{9k#M9|8(Q_jS+SihQ14HLHW-$G#qc&{m*KT}X9&fRJyWwiLMFw8D0s1a4 zXBoBO!&W!8-HCUYw1{+d_PQ3b3qtFm(}Cq7Qb?mzt-&u&u(OO$uBqvo5O`Ez1q|J8 z%t*qHk}2w;CS+W-cAvbkWV2aUhLXf*VCZea3~^d{T$-eR8D8Wz$0spWZFn|pH*O&B z$i>W^guGJZ;TeA?dedj+HO&%p7Vn0=kTV#x9(pyHvFnWrwUN&Fo<%pM>a?p%VG^U% z%`>>sjv0$2ZK#bAN0Z~mgJDDL*AGqK{d^HwPoa72Wx$N3qcqy;a5A?~dc9|9uR~73 z*6b)`wTBr<3m{iWd_VF^QI8`B&hgw9N<1jg;gTW6-0cYreI8UGabs^3Mti?TsGrRA z+MFgf>*foGt8oMLEB;Iy#G^2rwhvZ{-kzK3sS?d<81)eVh7~AP+NcV(q2zh^ei*6L zXqj87)y^yL>w#enO2vcy;D92JUjavUM9LIMeCYeIN@ABYvIa$K?(+#rn?zDjHK5L8 z=F^*?aHbmpy3XHTKljZJ0>-7Km|=51kJ@y{?uWA)Ik47C~gQMktEWh z?`71+_RycsOBEOkd@tv0kgvXV6F1~BgX}_u4`D1RV)vLRsu;cd2jz(ioPj~qZe1}==+A9Mj3_$>DOt>*A4O+r zikdH+>HqidCcxl>EE;}Mx7EU#{(pZvfjH${IMe^n?~5*+>Hqh)yy40A!kPYme5?5X z#+m-s)Ie=7m&>Ys~jV7%|Wp6TRY)e<)^7S|`G~ zoOWec{Jc+u|25`&B8(W#AdmT;2qOkq$;|si7%{-Eh~{Z6jqgUqA_iFd%=<)Gm(w1s zWN79=>qHnaQbEnJmz^K{I4PlW%ygY!KR{`U^f_e5BiGYGH8|Ctlv4rY%>+1;Iw3eI!y zAHT|f0o~o{a)u)3SjaW+6JcG>aBxyI?-OCfu*UM3_lYoKcwol7PlOQz*{37dyibI6 zIU`nK#=K92bvYyHFk{{)!iXV;8S_37MvOI>G4B&$!~kobd7lU)#wRR~d7lXDaz=r( zjCr33>vCe}?EixkVZ_*kl?p!*M!py+%Z>o@teB1Tl1GwJ*ncFMEe7v`>%jY)Q^-5= z6^eyxp#>INV4(#TT413C7FuAT1r}Ohp#>INV4(#TT413C7FuAT1r}Ohp#>INV4(#T zT413C7FuAT1r}Ohp#>INV4(#TT413C7FuAT1r}Ohp#>INV4(#TT413C7FuAT1r}Oh zp#>INV4(#TT413C7Fyu{l@`E%6hK})>(lY4ZprvdP_XR$IsBR}AI}}s{iX@{L1BXb z^^h-7t2E^Ryx0ixyqC!(xyktN8_&S_IsP~f-9NnlfcW?pB7QE-Hu{Lf$Q{5hPY|3W zy`(9BK%yV;0$zgC4Q<|8?l#T}xJ4Sl8Dw(aU04<22)J<~-G{kvq0MA%Pa7NMf zEZJLo-2u-kf;>dtQDmTD8Q>R63C={XOZu(;2mEbJQt>e4M#EK^8Or-Xybr7|QwXPH zi{BIc_eO^nQ@_^X?Lyox_y8eP8|_z`dm)=k`#Oc{tt=xSj;$ij{?K zVY7<>xA7(TVw0y{GwJNPfSaC#{OU+(t=+G5z^`%=oMT(;De*}ye;K<_+lIxJ9mfQOLW^TV2-&hhUiG z4f{X$A`R<*ViVBcydsx=9}&;9<_o`c;M{V+&0a!&G4Sqho4E$S!{PJG>s1`3Rag$!t&#Q?`wj5r?KkJ?}OlXkz_9T9PyoHk4)`f0e*8vic==y`QHkSU;FKi z|Hjd4J>*mD*AhQs|KA`a3nO@6mJGk0bW|*G0r_UZdI^Zgff_kHQQ+WYp)X7T|qI!MF|%Pc>t z)N#Q9aFchCAM?=8k~_={c<^n4i`+lbR6 z3(8M2gZFDW(^;k5kqyXi64A@1got0xX0b~?uSp#6lQ$vPTO$9^qrM;TPyxtqF{@}- z@N5BmzX{~y$H#;(w#NhRYDI8Sp?lAy#}0vhMdl8M{F-B=m_!Qr-6AP^jNoFQr#mcG zjNa3?q~NLsa&m1V}@_v=hBk&a~6vE;_RGWfWf`<38QM$H!L z9ePj1 zx%fQjQ%>-8OJ{%Fy1cm*#3z~&d_7&pvQ*z9{MWs%_aU!tXI<_e4t~{0+7I)U(W$xY z%CSfnq~jDy#LG0y?Dtt^fsfC}L?Qol>&klW&**Q^x;2XYK)|A+lNdjIB+edbz z^(l)w)`}fE#Tj;uR9Ma&H)Xi?2^?_#r=QKfQ^unir3U?X2+qkA;n>>1^BPmu6IDiI>ws8uBa09TfZS;?K1%e6F|Gu48m&R>$Yx zOf4dQhx+8}>aK(&kbWL~UUnFe4dRErY5>n&OT;UQ$Sq;`^Q{8oWFA;ArK>Tug;y%^ zAMvKb{-D$v68-em2wr|2$W{IwUFtQ$U(Mz2z93A~wJej=(a;c-g-Rkc0 zgLLd4Lw-c1wak>|F5szfTvfk+qes|N2!Fl`PQv)#!++>rHzI$v554vgcy7-M#;oum zu5Ex{g3sw5*YqP(b-DQZC*dI>Xe$2Dd;JLgzkQ-t!a;&-hslRN4a^q?JSvgkx;Z?Z zJ{Ar5KEe~GuRH8_p|F$x1BiDVA>#GO+p@-c-5CKtHUzoTWur1pdso22xFK)S^>DnJ zfIp{J)db&bdETPv#TJ~Wz<#o~uH0Y1wh{kOs2ultsjLCJAE-=g&dH+mDUEdTx&@)wrS%M*@2#zumy zHIb3e0pAPP3C8ZN-*=dbHvygx>t)=+x!5J`tQ6o^%8B%izwNGK(5S@6pAeYNe%7S% zDV>e@Jmvb8h~NLo=<+{uj<-QNg>YSWKWtn`WnO+!G35Wq{#oou)6BRvsttu`VZQ_GO+68A?o0q%DZ@|RmuRa?c60bb}1dA~#&@1||0fLl%z+)P3& z`dA$7v9sHbrntId-W$n*w}2W4spft%j8cN`Byf^x*tPf?F*% zuWM%F#MhaZVL!1-x|MR!C=h?X4t2r!4~>yp4sLimC;JlIn*TbpB;!?fP@c1}zSe@N zKHh%C_;XSC62@Cetl2Am)BwaEh2xm@yO#LG8x9kI`<)=-ZP-~3_X%d<>z^yakn_}+ zY`kA#1LC!xL+{5CH&Z4}HN=mCB-1;)RBbog27IgR0SWS=l4%v3J) z7Np~>M5JTiKN-xE&IIODQXU*n95?1|y`?MfkCz9ommCe#m;OARh0mwQ|5tn08WYtK zMj_ZrQtQPATGvNb+t^5p)rQzdwq&uz#L`<47g1J36f3X@2*|>^3ng8UvRj@KQY>h& z4=69Snvg0skQ8~Ns3F9LV5(UPRjiG5TT*O?`DQ+o#vk*uKZa!4w{SH;x zO2j9t7mut+G628!DUK&(8Hz{Fx!Cn)MHAwEv9H$`?wAF>1^Y9xQCArr(Q^av&tKwv zQ>RhC!!W`71pC(1{brb5S^6^YvkaV{WL{PB=SUP=$1GSk>E7&EK}bBi4y{S$`~y*I z^xmckHXrlC5FhZ&S#Z_G_Gu|a98dP%oVl*Oc>?N2>_XhH;_TY5{J#X;iR)o<>Lb_4 zaP41!@AO1|+uz3fS{s|c@z_7fU8d5in0f5`xAY$JFI{Z-`E*?{9mjLwZp5dR$kVq@ zv3+$kuAeEb&-7+!!&{(k6Z(+yz&GHP*~Kpap0kIKlNy&f;B-F(zURVn#0%Vav**uT zhq})OBJS9{;l!$atgapV_Mq-c$C1#cD=@z48t12JVh4X2GO+zdI?l(m7k@bGu;#Mk zjG09KTkmyr4IVJVIL+ASX;JP+5#w)v19%?#mX>k+xs>SsyMUXcaGcJ1U;Sl0^Lg(u z;(v?|=3kFt*GHk%98V7npY3(GPQo}laX*>f8eLMBWMu2P*~$4CF|W1WH&*okKhlo) zw3l!CiC5XaE35@^clp&4Q^9iJ7vMU7NL##SYvL%Ik2WvP&m6J;)UxDQI`H@EIBqr$ z?YdZ2%g$rr1&(KZwAM@K8O+w_ES#s=fsS{B{Ce4bwFuXT>^tEd%QWY83KJ;m06A`Rl%K|bT{UDtrW?mWRg++q`%fMn#={WD`8nD#y?dSCZNSU0&vP2Jb>8O_o(DYFpYwB@iidK45wj_>p*=d^`^i&HM0~{4X7l4oC;21JVKM zfOJ4QARUknNC%_?(gEp!bU->F9gq%42c!eif&Zrif#k6K$!q1@%jZeiv8qy4MUP!L zJC-QmnI_@vSXDXGKx+tR$EwPuKB^GTj#ZWJB~&4t9ZM7yR3V%lOB8pgLO45CReDxa z#e(jeZJPs`M?R3gPTnqA*j1aCR(FR8oa-b}Ugqw+m;-62%Q#LpVEDRr>c+g>ZJPsti0$ z6~fuEL;?3eg|lOcLZvl?vtx-OmMVm^V~GOpND61i5=9NIA)Fmc6hBghaCR(F!2Lww O>{wM9q$NC9cI>}7BmIT| diff --git a/2022-2023/main-bot/.gradle/7.5.1/fileHashes/resourceHashesCache.bin b/2022-2023/main-bot/.gradle/7.5.1/fileHashes/resourceHashesCache.bin deleted file mode 100755 index 7df2b9817eeec5b19f6fb6c2cd69be618d302035..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34545 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 z zM851hE(sgz+`{@Es_V!YWh)XVHpH=rSr>Vr*7Rq@}a!PZlc$4!Q6-NkRlTHE34M~#QlK2Y2x?^gmkL6{~ zH(AJ5PU`D6R2SneysS2Cx$=Xx9BHIs7DRIPQgvy%I*#M*X`~~K1EQ_s=`x$A7Q|CY znM}TKkVb!tNkEMN?>=Tx&B+I@r6|>St_MMUOPf$)#F*FvDwW1TWQ=^#@Hc7Q8=ND3(?zX;*?H^b)f8&RLx`DFx_1}HA@0r5B z;;}0;UFYMDQcGby=wEj{xHx4*9edWQaS!FB)w|uLot#C75iR-h-U*#R6=Y8-96jjE z#?Oed5e`l@Bp#vcl{s^@PUi`PrCMMA-j{j*G!GfWB2hSI=7A7K>P(-vkhBpu(s(>0 zS9Ic%xL@C{j;i8>ILary1P%++E7m`;M|XeQUhvEmLdLKU3Jw^&?R>w}6@&PNiG&H# zAb%k)|NU@6qk^(IehK5;Z=`Y1ymPd#zxwRX1`h_;+yEZZI2#oIsnwoUVbEmL$<@0N zWfyxB;%Ic&O5nuZ(9zq@QNDMOF>q(8*H1Y(Ug4saS-Hz2B8@b798Tv~=6{UaD({-~ zGyaNFOU36hB(GaLa#YY5FJ~ww`XOU9KDfSNYc%yldJmg>CQKA1e^66a?eO8sTzz29 zFIbWhic&c$reU!Wu)N>X%9xO1I&xM=2mMTZmRRL*?NQ}%$J(G|! z5S>4>Qu7lff2c zkO*;o801epwKk3Hhi;8D8J)^R|I^5KnqNu`%#g+hmg^Z?BOLEbUSURMb~m8h2>9P* zF{Q>C_1@ZiYj!?Z6eXX&e8Y&*EPRnM@G=uWDH6%_juyyrM?!!scc3CK`ErswRVegCG1kfiH=zenRs24 zo0{IzjM?i+>4dU&ydQKg(xTj;{gNx@5yPb(yGfW|cJ^f#%CmT2&PNK*NPIl;xjdkb zZ4TvGzUKRP4?RdP&gUUsRX=h^*O Ts0eXgVNo|BY(u~QnT7uc>KTSB diff --git a/2022-2023/main-bot/.gradle/buildOutputCleanup/outputFiles.bin b/2022-2023/main-bot/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100755 index eae504c0365fdf92460e0c0a670dea8fce07db38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24131 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鯊%1Et zwk~hMkhq;MOw^Q5`7oC{CUUyTw9x>^%*FHx^qJw(LB+E0WBX@{Ghw;)6aA-KyYg8p z7XDveQOpEr;B4je@2~usI5BlFadedX^ma{b{ypd|RNYqo#~d*mj&y`^iojR}s%~vF z(H!u`yx68D1Tj(3(m;Q+Ma}s2n#;O~bcB1`lYk%Irx60&-nWIUBr2x&@}@76+*zJ5 ze&4?q8?m%L9c6h=J$WBzbiTf1Z-0Eb5$IZs>lvm$>1n_Mezp*qw_pr8<8$6f)5f<@ zyV#tzMCs51nTv_5ca`x`yfE5YA^*%O_H?;tWYdM_kHPubA%vy47i=9>Bq) zRQ&0UwLQHeswmB1yP)+BiR;S+Vc-5TX84KUA;8VY9}yEj0eESSO`7HQ4lO z4(CyA8y1G7_C;6kd4U3K-aNOK!sHE}KL_-^EDl(vB42P$2Km7$WGqNy=%fqB+ zSLdrlcbEH=T@W8V4(TgoXZ*G1_aq$K^@ek=TVhoKRjw;HyI&coln|uRr5mMOy2GXP zwr*F^Y|!Sjr2YQXX(Fp^*`Wk905K%$bd03R4(igl0&7IIm*#f`A!DCarW9$h$z`kYk9MjjqN&5-DsH@8xh63!fTNPxWsFQhNv z#|3RjnP$Thdb#Ys7M+v|>AHm0BVTw)EH}>x@_f4zca&3tXJhTZ8pO}aN?(dHo)44Z z_5j+YP=jMlFqwvf3lq!57-SAuRV2_gJ*wsR_!Y4Z(trO}0wmB9%f#jNDHPdQGHFR; zZXzS-$`;7DQ5vF~oSgP3bNV$6Z(rwo6W(U07b1n3UHqml>{=6&-4PALATsH@Bh^W? z)ob%oAPaiw{?9HfMzpGb)@Kys^J$CN{uf*HX?)z=g`J(uK1YO^8~s1(ZIbG%Et(|q z$D@_QqltVZu9Py4R0Ld8!U|#`5~^M=b>fnHthzKBRr=i+w@0Vr^l|W;=zFT#PJ?*a zbC}G#It}rQP^Ait^W&aa6B;+0gNvz4cWUMzpv(1gvfw-X4xJ2Sv;mt;zb2Tsn|kSS zo*U9N?I{=-;a-OybL4r;PolCfiaL=y@o9{%`>+&FI#D^uy#>)R@b^1ue&AKKwuI*` zx%+6r48EIX6nF4o;>)zhV_8(IEX})NGU6Vs(yslrx{5fII}o3SMHW7wGtK9oIO4OM&@@ECtXSICLcPXoS|{;=_yj>hh*%hP27yZwOmj4&Lh z*Nd@OMkd!aKReoqNOkp5cW*lC)&C$P?+H3*%8)6HcpBg&IhGP^77XPZpc%WKYLX$T zsSQ$|ntaVVOoRat$6lvZO(G-QM5s#N4j*|N_;8cc2v_k4n6zx9c1L4JL*83F-C1Cn zaJhd;>rHXB%%ZN=3_o3&Qd2YOxrK~&?1=UuN9QhL$~OY-Qyg&})#ez*8NpQW_*a&kD&ANjedxT0Ar z<6r{eaVz3`d~+N~vkMaV8{F?RBVemN(jD@S8qO~L{rUw#=2a$V(7rLE+kGUZ<%pdr z?$DP|Vg#gZ9S}w((O2NbxzQ^zTot=89!0^~hE{|c9q1hVzv0?YC5s42Yx($;hAp*E zyoGuRyphQY{Q2ee0Xx`1&lv(l-SeC$NEyS~8iil3_aNlnqF_G|;zt#F%1;J)jnPT& z@iU0S;wHJ2$f!juqEzPZeZkjcQ+Pa@eERSLKsWf=`{R@yv7AuRh&ALRTAy z8=g&nxsSJCe!QLchJ=}6|LshnXIK)SNd zRkJNiqHwKK{SO;N5m5wdL&qK`v|d?5<4!(FAsDxR>Ky#0#t$8XCMptvNo?|SY?d8b z`*8dVBlXTUanlh6n)!EHf2&PDG8sXNAt6~u-_1EjPI1|<=33T8 zEnA00E!`4Ave0d&VVh0e>)Dc}=FfAFxpsC1u9ATfQ`-Cu;mhc8Z>2;uyXtqpLb7(P zd2F9<3cXS} znMg?{&8_YFTGRQZEPU-XPq55%51}RJpw@LO_|)CFAt62-_!u_Uq$csc+7|3+TV_!h z+2a7Yh^5AA{q^m|=KSJL+w-EWDBc&I_I1vOr^}P8i?cKMhGy$CP0XKrQzCheG$}G# zuglf8*PAFO8%xop7KSwI8||liTaQ9NCAFarr~psQt)g*pC@9bORZ>m`_GA`_K@~&% zijH0z;T$fd;-Liw8%EKZas>BH8nYTqsK7F;>>@YsE=Rqo?_8}UO-S#|6~CAW0Oz1} z3F(1=+#wrBJh4H)9jTQ_$~@#9|Bc1Pd3rAIA_&vOpvvbgDJOM(yNPhJJq2%PCcMaI zrbe~toYzvkZYQ{ea(Wiyu#4WB#RRN%bMe=SOk!CbJZv^m?Flo5p{W8|0i3`hI3Np# zvCZqY%o258CI=SGb+A3yJe~JH^i{uU`#U#fvSC~rWTq+K`E%J@ zasU07&pB6A4w3b?d?q}2=0rA#SA7D`X+zg@&zm^iA*HVi z009#PUH<%lk4z~p^l0S{lCJk1Uxi=F4e_DwlfHA`X`rv(|JqWKAA5nH+u4Da+E_p+ zVmH@lg^n4ixs~*@gm_dgQ&eDmE1mnw5wBz9Yg?QdZwF|an67Xd*x!He)Gc8&2!urh z4_uXzbYz-aX)X1>&iUjGp;P1u8&7TID0bTH-jCL&Xk8b&;;6p2op_=y^m@Nq*0{#o!!A;wNAFG@0%Z9rHo zcJs?Th>Ny6+hI`+1XoU*ED$Yf@9f91m9Y=#N(HJP^Y@ZEYR6I?oM{>&Wq4|v0IB(p zqX#Z<_3X(&{H+{3Tr|sFy}~=bv+l=P;|sBz$wk-n^R`G3p0(p>p=5ahpaD7>r|>pm zv;V`_IR@tvZreIuv2EM7ZQHhO+qUgw#kOs%*ekY^n|=1#x9&c;Ro&I~{rG-#_3ZB1 z?|9}IFdbP}^DneP*T-JaoYHt~r@EfvnPE5EKUwIxjPbsr$% zfWW83pgWST7*B(o=kmo)74$8UU)v0{@4DI+ci&%=#90}!CZz|rnH+Mz=HN~97G3~@ z;v5(9_2%eca(9iu@J@aqaMS6*$TMw!S>H(b z4(*B!|H|8&EuB%mITr~O?vVEf%(Gr)6E=>H~1VR z&1YOXluJSG1!?TnT)_*YmJ*o_Q@om~(GdrhI{$Fsx_zrkupc#y{DK1WOUR>tk>ZE) ziOLoBkhZZ?0Uf}cm>GsA>Rd6V8@JF)J*EQlQ<=JD@m<)hyElXR0`pTku*3MU`HJn| zIf7$)RlK^pW-$87U;431;Ye4Ie+l~_B3*bH1>*yKzn23cH0u(i5pXV! z4K?{3oF7ZavmmtTq((wtml)m6i)8X6ot_mrE-QJCW}Yn!(3~aUHYG=^fA<^~`e3yc z-NWTb{gR;DOUcK#zPbN^D*e=2eR^_!(!RKkiwMW@@yYtEoOp4XjOGgzi`;=8 zi3`Ccw1%L*y(FDj=C7Ro-V?q)-%p?Ob2ZElu`eZ99n14-ZkEV#y5C+{Pq87Gu3&>g zFy~Wk7^6v*)4pF3@F@rE__k3ikx(hzN3@e*^0=KNA6|jC^B5nf(XaoQaZN?Xi}Rn3 z$8&m*KmWvPaUQ(V<#J+S&zO|8P-#!f%7G+n_%sXp9=J%Z4&9OkWXeuZN}ssgQ#Tcj z8p6ErJQJWZ+fXLCco=RN8D{W%+*kko*2-LEb))xcHwNl~Xmir>kmAxW?eW50Osw3# zki8Fl$#fvw*7rqd?%E?}ZX4`c5-R&w!Y0#EBbelVXSng+kUfeUiqofPehl}$ormli zg%r)}?%=?_pHb9`Cq9Z|B`L8b>(!+8HSX?`5+5mm81AFXfnAt1*R3F z%b2RPIacKAddx%JfQ8l{3U|vK@W7KB$CdLqn@wP^?azRks@x8z59#$Q*7q!KilY-P zHUbs(IFYRGG1{~@RF;Lqyho$~7^hNC`NL3kn^Td%A7dRgr_&`2k=t+}D-o9&C!y^? z6MsQ=tc3g0xkK(O%DzR9nbNB(r@L;1zQrs8mzx&4dz}?3KNYozOW5;=w18U6$G4U2 z#2^qRLT*Mo4bV1Oeo1PKQ2WQS2Y-hv&S|C7`xh6=Pj7MNLC5K-zokZ67S)C;(F0Dd zloDK2_o1$Fmza>EMj3X9je7e%Q`$39Dk~GoOj89-6q9|_WJlSl!!+*{R=tGp z8u|MuSwm^t7K^nUe+^0G3dkGZr3@(X+TL5eah)K^Tn zXEtHmR9UIaEYgD5Nhh(s*fcG_lh-mfy5iUF3xxpRZ0q3nZ=1qAtUa?(LnT9I&~uxX z`pV?+=|-Gl(kz?w!zIieXT}o}7@`QO>;u$Z!QB${a08_bW0_o@&9cjJUXzVyNGCm8 zm=W+$H!;_Kzp6WQqxUI;JlPY&`V}9C$8HZ^m?NvI*JT@~BM=()T()Ii#+*$y@lTZBkmMMda>7s#O(1YZR+zTG@&}!EXFG{ zEWPSDI5bFi;NT>Yj*FjH((=oe%t%xYmE~AGaOc4#9K_XsVpl<4SP@E!TgC0qpe1oi zNpxU2b0(lEMcoibQ-G^cxO?ySVW26HoBNa;n0}CWL*{k)oBu1>F18X061$SP{Gu67 z-v-Fa=Fl^u3lnGY^o5v)Bux}bNZ~ z5pL+7F_Esoun8^5>z8NFoIdb$sNS&xT8_|`GTe8zSXQzs4r^g0kZjg(b0bJvz`g<70u9Z3fQILX1Lj@;@+##bP|FAOl)U^9U>0rx zGi)M1(Hce)LAvQO-pW!MN$;#ZMX?VE(22lTlJrk#pB0FJNqVwC+*%${Gt#r_tH9I_ z;+#)#8cWAl?d@R+O+}@1A^hAR1s3UcW{G+>;X4utD2d9X(jF555}!TVN-hByV6t+A zdFR^aE@GNNgSxxixS2p=on4(+*+f<8xrwAObC)D5)4!z7)}mTpb7&ofF3u&9&wPS< zB62WHLGMhmrmOAgmJ+|c>qEWTD#jd~lHNgT0?t-p{T=~#EMcB| z=AoDKOL+qXCfk~F)-Rv**V}}gWFl>liXOl7Uec_8v)(S#av99PX1sQIVZ9eNLkhq$ zt|qu0b?GW_uo}TbU8!jYn8iJeIP)r@;!Ze_7mj{AUV$GEz6bDSDO=D!&C9!M@*S2! zfGyA|EPlXGMjkH6x7OMF?gKL7{GvGfED=Jte^p=91FpCu)#{whAMw`vSLa`K#atdN zThnL+7!ZNmP{rc=Z>%$meH;Qi1=m1E3Lq2D_O1-X5C;!I0L>zur@tPAC9*7Jeh)`;eec}1`nkRP(%iv-`N zZ@ip-g|7l6Hz%j%gcAM}6-nrC8oA$BkOTz^?dakvX?`^=ZkYh%vUE z9+&)K1UTK=ahYiaNn&G5nHUY5niLGus@p5E2@RwZufRvF{@$hW{;{3QhjvEHMvduO z#Wf-@oYU4ht?#uP{N3utVzV49mEc9>*TV_W2TVC`6+oI)zAjy$KJrr=*q##&kobiQ z1vNbya&OVjK`2pdRrM?LuK6BgrLN7H_3m z!qpNKg~87XgCwb#I=Q&0rI*l$wM!qTkXrx1ko5q-f;=R2fImRMwt5Qs{P*p^z@9ex z`2#v(qE&F%MXlHpdO#QEZyZftn4f05ab^f2vjxuFaat2}jke{j?5GrF=WYBR?gS(^ z9SBiNi}anzBDBRc+QqizTTQuJrzm^bNA~A{j%ugXP7McZqJ}65l10({wk++$=e8O{ zxWjG!Qp#5OmI#XRQQM?n6?1ztl6^D40hDJr?4$Wc&O_{*OfMfxe)V0=e{|N?J#fgE>j9jAajze$iN!*yeF%jJU#G1c@@rm zolGW!j?W6Q8pP=lkctNFdfgUMg92wlM4E$aks1??M$~WQfzzzXtS)wKrr2sJeCN4X zY(X^H_c^PzfcO8Bq(Q*p4c_v@F$Y8cHLrH$`pJ2}=#*8%JYdqsqnGqEdBQMpl!Ot04tUGSXTQdsX&GDtjbWD=prcCT9(+ z&UM%lW%Q3yrl1yiYs;LxzIy>2G}EPY6|sBhL&X&RAQrSAV4Tlh2nITR?{6xO9ujGu zr*)^E`>o!c=gT*_@6S&>0POxcXYNQd&HMw6<|#{eSute2C3{&h?Ah|cw56-AP^f8l zT^kvZY$YiH8j)sk7_=;gx)vx-PW`hbSBXJGCTkpt;ap(}G2GY=2bbjABU5)ty%G#x zAi07{Bjhv}>OD#5zh#$0w;-vvC@^}F! z#X$@)zIs1L^E;2xDAwEjaXhTBw2<{&JkF*`;c3<1U@A4MaLPe{M5DGGkL}#{cHL%* zYMG+-Fm0#qzPL#V)TvQVI|?_M>=zVJr9>(6ib*#z8q@mYKXDP`k&A4A};xMK0h=yrMp~JW{L?mE~ph&1Y1a#4%SO)@{ zK2juwynUOC)U*hVlJU17%llUxAJFuKZh3K0gU`aP)pc~bE~mM!i1mi!~LTf>1Wp< zuG+ahp^gH8g8-M$u{HUWh0m^9Rg@cQ{&DAO{PTMudV6c?ka7+AO& z746QylZ&Oj`1aqfu?l&zGtJnpEQOt;OAFq19MXTcI~`ZcoZmyMrIKDFRIDi`FH)w; z8+*8tdevMDv*VtQi|e}CnB_JWs>fhLOH-+Os2Lh!&)Oh2utl{*AwR)QVLS49iTp{6 z;|172Jl!Ml17unF+pd+Ff@jIE-{Oxv)5|pOm@CkHW?{l}b@1>Pe!l}VccX#xp@xgJ zyE<&ep$=*vT=}7vtvif0B?9xw_3Gej7mN*dOHdQPtW5kA5_zGD zpA4tV2*0E^OUimSsV#?Tg#oiQ>%4D@1F5@AHwT8Kgen$bSMHD3sXCkq8^(uo7CWk`mT zuslYq`6Yz;L%wJh$3l1%SZv#QnG3=NZ=BK4yzk#HAPbqXa92;3K5?0kn4TQ`%E%X} z&>Lbt!!QclYKd6+J7Nl@xv!uD%)*bY-;p`y^ZCC<%LEHUi$l5biu!sT3TGGSTPA21 zT8@B&a0lJHVn1I$I3I1I{W9fJAYc+8 zVj8>HvD}&O`TqU2AAb={?eT;0hyL(R{|h23=4fDSZKC32;wWxsVj`P z3J3{M$PwdH!ro*Cn!D&=jnFR>BNGR<<|I8CI@+@658Dy(lhqbhXfPTVecY@L8%`3Q z1Fux2w?2C3th60jI~%OC9BtpNF$QPqcG+Pz96qZJ71_`0o0w_q7|h&O>`6U+^BA&5 zXd5Zp1Xkw~>M%RixTm&OqpNl8Q+ue=92Op_>T~_9UON?ZM2c0aGm=^A4ejrXj3dV9 zhh_bCt-b9`uOX#cFLj!vhZ#lS8Tc47OH>*)y#{O9?AT~KR9LntM|#l#Dlm^8{nZdk zjMl#>ZM%#^nK2TPzLcKxqx24P7R1FPlBy7LSBrRvx>fE$9AJ;7{PQm~^LBX^k#6Zq zw*Z(zJC|`!6_)EFR}8|n8&&Rbj8y028~P~sFXBFRt+tmqH-S3<%N;C&WGH!f3{7cm zy_fCAb9@HqaXa1Y5vFbxWf%#zg6SI$C+Uz5=CTO}e|2fjWkZ;Dx|84Ow~bkI=LW+U zuq;KSv9VMboRvs9)}2PAO|b(JCEC_A0wq{uEj|3x@}*=bOd zwr{TgeCGG>HT<@Zeq8y}vTpwDg#UBvD)BEs@1KP$^3$sh&_joQPn{hjBXmLPJ{tC) z*HS`*2+VtJO{|e$mM^|qv1R*8i(m1`%)}g=SU#T#0KlTM2RSvYUc1fP+va|4;5}Bfz98UvDCpq7}+SMV&;nX zQw~N6qOX{P55{#LQkrZk(e5YGzr|(B;Q;ju;2a`q+S9bsEH@i1{_Y0;hWYn1-79jl z5c&bytD*k)GqrVcHn6t-7kinadiD>B{Tl`ZY@`g|b~pvHh5!gKP4({rp?D0aFd_cN zhHRo4dd5^S6ViN(>(28qZT6E>??aRhc($kP`>@<+lIKS5HdhjVU;>f7<4))E*5|g{ z&d1}D|vpuV^eRj5j|xx9nwaCxXFG?Qbjn~_WSy=N}P0W>MP zG-F%70lX5Xr$a)2i6?i|iMyM|;Jtf*hO?=Jxj12oz&>P=1#h~lf%#fc73M2_(SUM- zf&qnjS80|_Y0lDgl&I?*eMumUklLe_=Td!9G@eR*tcPOgIShJipp3{A10u(4eT~DY zHezEj8V+7m!knn7)W!-5QI3=IvC^as5+TW1@Ern@yX| z7Nn~xVx&fGSr+L%4iohtS3w^{-H1A_5=r&x8}R!YZvp<2T^YFvj8G_vm}5q;^UOJf ztl=X3iL;;^^a#`t{Ae-%5Oq{?M#s6Npj+L(n-*LMI-yMR{)qki!~{5z{&`-iL}lgW zxo+tnvICK=lImjV$Z|O_cYj_PlEYCzu-XBz&XC-JVxUh9;6*z4fuBG+H{voCC;`~GYV|hj%j_&I zDZCj>Q_0RCwFauYoVMiUSB+*Mx`tg)bWmM^SwMA+?lBg12QUF_x2b)b?qb88K-YUd z0dO}3k#QirBV<5%jL$#wlf!60dizu;tsp(7XLdI=eQs?P`tOZYMjVq&jE)qK*6B^$ zBe>VvH5TO>s>izhwJJ$<`a8fakTL!yM^Zfr2hV9`f}}VVUXK39p@G|xYRz{fTI+Yq z20d=)iwjuG9RB$%$^&8#(c0_j0t_C~^|n+c`Apu|x7~;#cS-s=X1|C*YxX3ailhg_|0`g!E&GZJEr?bh#Tpb8siR=JxWKc{#w7g zWznLwi;zLFmM1g8V5-P#RsM@iX>TK$xsWuujcsVR^7TQ@!+vCD<>Bk9tdCo7Mzgq5 zv8d>dK9x8C@Qoh01u@3h0X_`SZluTb@5o;{4{{eF!-4405x8X7hewZWpz z2qEi4UTiXTvsa(0X7kQH{3VMF>W|6;6iTrrYD2fMggFA&-CBEfSqPlQDxqsa>{e2M z(R5PJ7uOooFc|9GU0ELA%m4&4Ja#cQpNw8i8ACAoK6?-px+oBl_yKmenZut#Xumjz zk8p^OV2KY&?5MUwGrBOo?ki`Sxo#?-Q4gw*Sh0k`@ zFTaYK2;}%Zk-68`#5DXU$2#=%YL#S&MTN8bF+!J2VT6x^XBci6O)Q#JfW{YMz) zOBM>t2rSj)n#0a3cjvu}r|k3od6W(SN}V-cL?bi*Iz-8uOcCcsX0L>ZXjLqk zZu2uHq5B|Kt>e+=pPKu=1P@1r9WLgYFq_TNV1p9pu0erHGd!+bBp!qGi+~4A(RsYN@CyXNrC&hxGmW)u5m35OmWwX`I+0yByglO`}HC4nGE^_HUs^&A(uaM zKPj^=qI{&ayOq#z=p&pnx@@k&I1JI>cttJcu@Ihljt?6p^6{|ds`0MoQwp+I{3l6` zB<9S((RpLG^>=Kic`1LnhpW2=Gu!x`m~=y;A`Qk!-w`IN;S8S930#vBVMv2vCKi}u z6<-VPrU0AnE&vzwV(CFC0gnZYcpa-l5T0ZS$P6(?9AM;`Aj~XDvt;Jua=jIgF=Fm? zdp=M$>`phx%+Gu};;-&7T|B1AcC#L4@mW5SV_^1BRbo6;2PWe$r+npRV`yc;T1mo& z+~_?7rA+(Um&o@Tddl zL_hxvWk~a)yY}%j`Y+200D%9$bWHy&;(yj{jpi?Rtz{J66ANw)UyPOm;t6FzY3$hx zcn)Ir79nhFvNa7^a{SHN7XH*|Vlsx`CddPnA&Qvh8aNhEA;mPVv;Ah=k<*u!Zq^7 z<=xs*iQTQOMMcg|(NA_auh@x`3#_LFt=)}%SQppP{E>mu_LgquAWvh<>L7tf9+~rO znwUDS52u)OtY<~!d$;m9+87aO+&`#2ICl@Y>&F{jI=H(K+@3M1$rr=*H^dye#~TyD z!){#Pyfn+|ugUu}G;a~!&&0aqQ59U@UT3|_JuBlYUpT$2+11;}JBJ`{+lQN9T@QFY z5+`t;6(TS0F?OlBTE!@7D`8#URDNqx2t6`GZ{ZgXeS@v%-eJzZOHz18aS|svxII$a zZeFjrJ*$IwX$f-Rzr_G>xbu@euGl)B7pC&S+CmDJBg$BoV~jxSO#>y z33`bupN#LDoW0feZe0%q8un0rYN|eRAnwDHQ6e_)xBTbtoZtTA=Fvk){q}9Os~6mQ zKB80VI_&6iSq`LnK7*kfHZoeX6?WE}8yjuDn=2#JG$+;-TOA1%^=DnXx%w{b=w}tS zQbU3XxtOI8E(!%`64r2`zog;5<0b4i)xBmGP^jiDZ2%HNSxIf3@wKs~uk4%3Mxz;~ zts_S~E4>W+YwI<-*-$U8*^HKDEa8oLbmqGg?3vewnaNg%Mm)W=)lcC_J+1ov^u*N3 zXJ?!BrH-+wGYziJq2Y#vyry6Z>NPgkEk+Ke`^DvNRdb>Q2Nlr#v%O@<5hbflI6EKE z9dWc0-ORk^T}jP!nkJ1imyjdVX@GrjOs%cpgA8-c&FH&$(4od#x6Y&=LiJZPINVyW z0snY$8JW@>tc2}DlrD3StQmA0Twck~@>8dSix9CyQOALcREdxoM$Sw*l!}bXKq9&r zysMWR@%OY24@e`?+#xV2bk{T^C_xSo8v2ZI=lBI*l{RciPwuE>L5@uhz@{!l)rtVlWC>)6(G)1~n=Q|S!{E9~6*fdpa*n z!()-8EpTdj=zr_Lswi;#{TxbtH$8*G=UM`I+icz7sr_SdnHXrv=?iEOF1UL+*6O;% zPw>t^kbW9X@oEXx<97%lBm-9?O_7L!DeD)Me#rwE54t~UBu9VZ zl_I1tBB~>jm@bw0Aljz8! zXBB6ATG6iByKIxs!qr%pz%wgqbg(l{65DP4#v(vqhhL{0b#0C8mq`bnqZ1OwFV z7mlZZJFMACm>h9v^2J9+^_zc1=JjL#qM5ZHaThH&n zXPTsR8(+)cj&>Un{6v*z?@VTLr{TmZ@-fY%*o2G}*G}#!bmqpoo*Ay@U!JI^Q@7gj;Kg-HIrLj4}#ec4~D2~X6vo;ghep-@&yOivYP zC19L0D`jjKy1Yi-SGPAn94(768Tcf$urAf{)1)9W58P`6MA{YG%O?|07!g9(b`8PXG1B1Sh0?HQmeJtP0M$O$hI z{5G`&9XzYhh|y@qsF1GnHN|~^ru~HVf#)lOTSrv=S@DyR$UKQk zjdEPFDz{uHM&UM;=mG!xKvp;xAGHOBo~>_=WFTmh$chpC7c`~7?36h)7$fF~Ii}8q zF|YXxH-Z?d+Q+27Rs3X9S&K3N+)OBxMHn1u(vlrUC6ckBY@@jl+mgr#KQUKo#VeFm zFwNYgv0<%~Wn}KeLeD9e1$S>jhOq&(e*I@L<=I5b(?G(zpqI*WBqf|Zge0&aoDUsC zngMRA_Kt0>La+Erl=Uv_J^p(z=!?XHpenzn$%EA`JIq#yYF?JLDMYiPfM(&Csr#f{ zdd+LJL1by?xz|D8+(fgzRs~(N1k9DSyK@LJygwaYX8dZl0W!I&c^K?7)z{2is;OkE zd$VK-(uH#AUaZrp=1z;O*n=b?QJkxu`Xsw&7yrX0?(CX=I-C#T;yi8a<{E~?vr3W> zQrpPqOW2M+AnZ&p{hqmHZU-;Q(7?- zP8L|Q0RM~sB0w1w53f&Kd*y}ofx@c z5Y6B8qGel+uT1JMot$nT1!Tim6{>oZzJXdyA+4euOLME?5Fd_85Uk%#E*ln%y{u8Q z$|?|R@Hpb~yTVK-Yr_S#%NUy7EBfYGAg>b({J|5b+j-PBpPy$Ns`PaJin4JdRfOaS zE|<HjH%NuJgsd2wOlv>~y=np%=2)$M9LS|>P)zJ+Fei5vYo_N~B0XCn+GM76 z)Xz3tg*FRVFgIl9zpESgdpWAavvVViGlU8|UFY{{gVJskg*I!ZjWyk~OW-Td4(mZ6 zB&SQreAAMqwp}rjy`HsG({l2&q5Y52<@AULVAu~rWI$UbFuZs>Sc*x+XI<+ez%$U)|a^unjpiW0l0 zj1!K0(b6$8LOjzRqQ~K&dfbMIE=TF}XFAi)$+h}5SD3lo z%%Qd>p9se=VtQG{kQ;N`sI)G^u|DN#7{aoEd zkksYP%_X$Rq08);-s6o>CGJ<}v`qs%eYf+J%DQ^2k68C%nvikRsN?$ap--f+vCS`K z#&~)f7!N^;sdUXu54gl3L=LN>FB^tuK=y2e#|hWiWUls__n@L|>xH{%8lIJTd5`w? zSwZbnS;W~DawT4OwSJVdAylbY+u5S+ZH{4hAi2&}Iv~W(UvHg(1GTZRPz`@{SOqzy z(8g&Dz=$PfRV=6FgxN~zo+G8OoPI&d-thcGVR*_^(R8COTM@bq?fDwY{}WhsQS1AK zF6R1t8!RdFmfocpJ6?9Yv~;WYi~XPgs(|>{5})j!AR!voO7y9&cMPo#80A(`za@t>cx<0;qxM@S*m(jYP)dMXr*?q0E`oL;12}VAep179uEr8c<=D zr5?A*C{eJ`z9Ee;E$8)MECqatHkbHH z&Y+ho0B$31MIB-xm&;xyaFCtg<{m~M-QDbY)fQ>Q*Xibb~8ytxZQ?QMf9!%cV zU0_X1@b4d+Pg#R!`OJ~DOrQz3@cpiGy~XSKjZQQ|^4J1puvwKeScrH8o{bscBsowomu z^f12kTvje`yEI3eEXDHJ6L+O{Jv$HVj%IKb|J{IvD*l6IG8WUgDJ*UGz z3!C%>?=dlfSJ>4U88)V+`U-!9r^@AxJBx8R;)J4Fn@`~k>8>v0M9xp90OJElWP&R5 zM#v*vtT}*Gm1^)Bv!s72T3PB0yVIjJW)H7a)ilkAvoaH?)jjb`MP>2z{%Y?}83 zUIwBKn`-MSg)=?R)1Q0z3b>dHE^)D8LFs}6ASG1|daDly_^lOSy&zIIhm*HXm1?VS=_iacG);_I9c zUQH1>i#*?oPIwBMJkzi_*>HoUe}_4o>2(SHWzqQ=;TyhAHS;Enr7!#8;sdlty&(>d zl%5cjri8`2X^Ds`jnw7>A`X|bl=U8n+3LKLy(1dAu8`g@9=5iw$R0qk)w8Vh_Dt^U zIglK}sn^)W7aB(Q>HvrX=rxB z+*L)3DiqpQ_%~|m=44LcD4-bxO3OO*LPjsh%p(k?&jvLp0py57oMH|*IMa(<|{m1(0S|x)?R-mqJ=I;_YUZA>J z62v*eSK;5w!h8J+6Z2~oyGdZ68waWfy09?4fU&m7%u~zi?YPHPgK6LDwphgaYu%0j zurtw)AYOpYKgHBrkX189mlJ`q)w-f|6>IER{5Lk97%P~a-JyCRFjejW@L>n4vt6#hq;!|m;hNE||LK3nw1{bJOy+eBJjK=QqNjI;Q6;Rp5 z&035pZDUZ#%Oa;&_7x0T<7!RW`#YBOj}F380Bq?MjjEhrvlCATPdkCTTl+2efTX$k zH&0zR1n^`C3ef~^sXzJK-)52(T}uTG%OF8yDhT76L~|^+hZ2hiSM*QA9*D5odI1>& z9kV9jC~twA5MwyOx(lsGD_ggYmztXPD`2=_V|ks_FOx!_J8!zM zTzh^cc+=VNZ&(OdN=y4Juw)@8-85lwf_#VMN!Ed(eQiRiLB2^2e`4dp286h@v@`O%_b)Y~A; zv}r6U?zs&@uD_+(_4bwoy7*uozNvp?bXFoB8?l8yG0qsm1JYzIvB_OH4_2G*IIOwT zVl%HX1562vLVcxM_RG*~w_`FbIc!(T=3>r528#%mwwMK}uEhJ()3MEby zQQjzqjWkwfI~;Fuj(Lj=Ug0y`>~C7`w&wzjK(rPw+Hpd~EvQ-ufQOiB4OMpyUKJhw zqEt~jle9d7S~LI~$6Z->J~QJ{Vdn3!c}g9}*KG^Kzr^(7VI5Gk(mHLL{itj_hG?&K4Ws0+T4gLfi3eu$N=`s36geNC?c zm!~}vG6lx9Uf^5M;bWntF<-{p^bruy~f?sk9 zcETAPQZLoJ8JzMMg<-=ju4keY@SY%Wo?u9Gx=j&dfa6LIAB|IrbORLV1-H==Z1zCM zeZcOYpm5>U2fU7V*h;%n`8 zN95QhfD994={1*<2vKLCNF)feKOGk`R#K~G=;rfq}|)s20&MCa65 zUM?xF5!&e0lF%|U!#rD@I{~OsS_?=;s_MQ_b_s=PuWdC)q|UQ&ea)DMRh5>fpQjXe z%9#*x=7{iRCtBKT#H>#v%>77|{4_slZ)XCY{s3j_r{tdpvb#|r|sbS^dU1x70$eJMU!h{Y7Kd{dl}9&vxQl6Jt1a` zHQZrWyY0?!vqf@u-fxU_@+}u(%Wm>0I#KP48tiAPYY!TdW(o|KtVI|EUB9V`CBBNaBLVih7+yMVF|GSoIQD0Jfb{ z!OXq;(>Z?O`1gap(L~bUcp>Lc@Jl-})^=6P%<~~9ywY=$iu8pJ0m*hOPzr~q`23eX zgbs;VOxxENe0UMVeN*>uCn9Gk!4siN-e>x)pIKAbQz!G)TcqIJ0`JBBaX>1-4_XO_-HCS^vr2vjv#7KltDZdyQ{tlWh4$Gm zB>|O1cBDC)yG(sbnc*@w6e%e}r*|IhpXckx&;sQCwGdKH+3oSG-2)Bf#x`@<4ETAr z0My%7RFh6ZLiZ_;X6Mu1YmXx7C$lSZ^}1h;j`EZd6@%JNUe=btBE z%s=Xmo1Ps?8G`}9+6>iaB8bgjUdXT?=trMu|4yLX^m0Dg{m7rpKNJey|EwHI+nN1e zL^>qN%5Fg)dGs4DO~uwIdXImN)QJ*Jhpj7$fq_^`{3fwpztL@WBB}OwQ#Epo-mqMO zsM$UgpFiG&d#)lzEQ{3Q;)&zTw;SzGOah-Dpm{!q7<8*)Ti_;xvV2TYXa}=faXZy? z3y?~GY@kl)>G&EvEijk9y1S`*=zBJSB1iet>0;x1Ai)*`^{pj0JMs)KAM=@UyOGtO z3y0BouW$N&TnwU6!%zS%nIrnANvZF&vB1~P5_d`x-giHuG zPJ;>XkVoghm#kZXRf>qxxEix;2;D1CC~NrbO6NBX!`&_$iXwP~P*c($EVV|669kDO zKoTLZNF4Cskh!Jz5ga9uZ`3o%7Pv`d^;a=cXI|>y;zC3rYPFLQkF*nv(r>SQvD*## z(Vo%^9g`%XwS0t#94zPq;mYGLKu4LU3;txF26?V~A0xZbU4Lmy`)>SoQX^m7fd^*E z+%{R4eN!rIk~K)M&UEzxp9dbY;_I^c} zOc{wlIrN_P(PPqi51k_$>Lt|X6A^|CGYgKAmoI#Li?;Wq%q~q*L7ehZkUrMxW67Jl zhsb~+U?33QS>eqyN{(odAkbopo=Q$Az?L+NZW>j;#~@wCDX?=L5SI|OxI~7!Pli;e zELMFcZtJY3!|=Gr2L4>z8yQ-{To>(f80*#;6`4IAiqUw`=Pg$%C?#1 z_g@hIGerILSU>=P>z{gM|DS91A4cT@PEIB^hSop!uhMo#2G;+tQSpDO_6nOnPWSLU zS;a9m^DFMXR4?*X=}d7l;nXuHk&0|m`NQn%d?8|Ab3A9l9Jh5s120ibWBdB z$5YwsK3;wvp!Kn@)Qae{ef`0#NwlRpQ}k^r>yos_Ne1;xyKLO?4)t_G4eK~wkUS2A&@_;)K0-03XGBzU+5f+uMDxC z(s8!8!RvdC#@`~fx$r)TKdLD6fWEVdEYtV#{ncT-ZMX~eI#UeQ-+H(Z43vVn%Yj9X zLdu9>o%wnWdvzA-#d6Z~vzj-}V3FQ5;axDIZ;i(95IIU=GQ4WuU{tl-{gk!5{l4_d zvvb&uE{%!iFwpymz{wh?bKr1*qzeZb5f6e6m_ozRF&zux2mlK=v_(_s^R6b5lu?_W4W3#<$zeG~Pd)^!4tzhs}-Sx$FJP>)ZGF(hVTH|C3(U zs0PO&*h_ zNA-&qZpTP$$LtIgfiCn07}XDbK#HIXdmv8zdz4TY;ifNIH-0jy(gMSByG2EF~Th#eb_TueZC` zE?3I>UTMpKQ})=C;6p!?G)M6w^u*A57bD?2X`m3X^6;&4%i_m(uGJ3Z5h`nwxM<)H z$I5m?wN>O~8`BGnZ=y^p6;0+%_0K}Dcg|K;+fEi|qoBqvHj(M&aHGqNF48~XqhtU? z^ogwBzRlOfpAJ+Rw7IED8lRbTdBdyEK$gPUpUG}j-M42xDj_&qEAQEtbs>D#dRd7Y z<&TpSZ(quQDHiCFn&0xsrz~4`4tz!CdL8m~HxZM_agu@IrBpyeL1Ft}V$HX_ZqDPm z-f89)pjuEzGdq-PRu`b1m+qBGY{zr_>{6Ss>F|xHZlJj9dt5HD$u`1*WZe)qEIuDSR)%z+|n zatVlhQ?$w#XRS7xUrFE;Y8vMGhQS5*T{ZnY=q1P?w5g$OKJ#M&e??tAmPWHMj3xhS ziGxapy?kn@$~2%ZY;M8Bc@%$pkl%Rvj!?o%agBvpQ-Q61n9kznC4ttrRNQ4%GFR5u zyv%Yo9~yxQJWJSfj z?#HY$y=O~F|2pZs22pu|_&Ajd+D(Mt!nPUG{|1nlvP`=R#kKH zO*s$r_%ss5h1YO7k0bHJ2CXN)Yd6CHn~W!R=SqkWe=&nAZu(Q1G!xgcUilM@YVei@2@a`8he z9@pM`)VB*=e7-MWgLlXlc)t;fF&-AwM{E-EX}pViFn0I0CNw2bNEnN2dj!^4(^zS3 zobUm1uQnpqk_4q{pl*n06=TfK_C>UgurKFjRXsK_LEn};=79`TB12tv6KzwSu*-C8 z;=~ohDLZylHQ|Mpx-?yql>|e=vI1Z!epyUpAcDCp4T|*RV&X`Q$0ogNwy6mFALo^@ z9=&(9txO8V@E!@6^(W0{*~CT>+-MA~vnJULBxCTUW>X5>r7*eXYUT0B6+w@lzw%n> z_VjJ<2qf|(d6jYq2(x$(ZDf!yVkfnbvNmb5c|hhZ^2TV_LBz`9w!e_V*W_(MiA7|= z&EeIIkw*+$Xd!)j8<@_<}A5;~A_>3JT*kX^@}cDoLd>Qj<`Se^wdUa(j0dp+Tl8EptwBm{9OGsdFEq zM`!pjf(Lm(`$e3FLOjqA5LnN5o!}z{ zNf}rJuZh@yUtq&ErjHeGzX4(!luV!jB&;FAP|!R_QHYw#^Z1LwTePAKJ6X&IDNO#; z)#I@Xnnzyij~C@UH~X51JCgQeF0&hTXnuoElz#m{heZRexWc0k4<>0+ClX7%0 zEBqCCld1tD9Zwkr4{?Nor19#E5-YKfB8d?qgR82-Ow2^AuNevly2*tHA|sK!ybYkX zm-sLQH72P&{vEAW6+z~O5d0qd=xW~rua~5a?ymYFSD@8&gV)E5@RNNBAj^C99+Z5Z zR@Pq55mbCQbz+Mn$d_CMW<-+?TU960agEk1J<>d>0K=pF19yN))a~4>m^G&tc*xR+yMD*S=yip-q=H zIlredHpsJV8H(32@Zxc@bX6a21dUV95Th--8pE6C&3F>pk=yv$yd6@Haw;$v4+Fcb zRwn{Qo@0`7aPa2LQOP}j9v>sjOo5Kqvn|`FLizX zB+@-u4Lw|jsvz{p^>n8Vo8H2peIqJJnMN}A)q6%$Tmig7eu^}K2 zrh$X?T|ZMsoh{6pdw1G$_T<`Ds-G=jc;qcGdK4{?dN2-XxjDNbb(7pk|3JUVCU4y; z)?LXR>f+AAu)JEiti_Zy#z5{RgsC}R(@jl%9YZ>zu~hKQ*AxbvhC378-I@{~#%Y`Z zy=a=9YpewPIC+gkEUUwtUL7|RU7=!^Aa}Mk^6uxOgRGA#JXjWLsjFUnix|Mau{hDT z7mn*z1m5g`vP(#tjT0Zy4eAY(br&!RiiXE=ZI!{sE1#^#%x^Z7t1U)b<;%Y}Q9=5v z;wpDCEZ@OE36TWT=|gxigT@VaW9BvHS05;_P(#s z8zI4XFQys}q)<`tkX$WnSarn{3e!s}4(J!=Yf>+Y>cP3f;vr63f2{|S^`_pWc)^5_!R z*(x-fuBxL51@xe!lnDBKi}Br$c$BMZ3%f2Sa6kLabiBS{pq*yj;q|k(86x`PiC{p6 z_bxCW{>Q2BA8~Ggz&0jkrcU+-$ANBsOop*ms>34K9lNYil@}jC;?cYP(m^P}nR6FV zk(M%48Z&%2Rx$A&FhOEirEhY0(dn;-k(qkTU)sFQ`+-ih+s@A8g?r8Pw+}2;35WYf zi}VO`jS`p(tc)$X$a>-#WXoW!phhatC*$}|rk>|wUU71eUJG^$c6_jwX?iSHM@6__ zvV|6%U*$sSXJu9SX?2%M^kK|}a2QJ8AhF{fuXrHZxXsI~O zGKX45!K7p*MCPEQ=gp?eu&#AW*pR{lhQR##P_*{c_DjMGL|3T3-bSJ(o$|M{ytU}> zAV>wq*uE*qFo9KvnA^@juy{x<-u*#2NvkV={Ly}ysKYB-k`K3@K#^S1Bb$8Y#0L0# z`6IkSG&|Z$ODy|VLS+y5pFJx&8tvPmMd8c9FhCyiU8~k6FwkakUd^(_ml8`rnl>JS zZV){9G*)xBqPz^LDqRwyS6w86#D^~xP4($150M)SOZRe9sn=>V#aG0Iy(_^YcPpIz8QYM-#s+n% z@Jd?xQq?Xk6=<3xSY7XYP$$yd&Spu{A#uafiIfy8gRC`o0nk{ezEDjb=q_qRAlR1d zFq^*9Gn)yTG4b}R{!+3hWQ+u3GT~8nwl2S1lpw`s0X_qpxv)g+JIkVKl${sYf_nV~B>Em>M;RlqGb5WVil(89 zs=ld@|#;dq1*vQGz=7--Br-|l) zZ%Xh@v8>B7P?~}?Cg$q9_={59l%m~O&*a6TKsCMAzG&vD>k2WDzJ6!tc!V)+oxF;h zJH;apM=wO?r_+*#;ulohuP=E>^zon}a$NnlcQ{1$SO*i=jnGVcQa^>QOILc)e6;eNTI>os=eaJ{*^DE+~jc zS}TYeOykDmJ=6O%>m`i*>&pO_S;qMySJIyP=}4E&J%#1zju$RpVAkZbEl+p%?ZP^C z*$$2b4t%a(e+%>a>d_f_<JjxI#J1x;=hPd1zFPx=6T$;;X1TD*2(edZ3f46zaAoW>L53vS_J*N8TMB|n+;LD| zC=GkQPpyDY#Am4l49chDv*gojhRj_?63&&8#doW`INATAo(qY#{q}%nf@eTIXmtU< zdB<7YWfyCmBs|c)cK>1)v&M#!yNj#4d$~pVfDWQc_ke1?fw{T1Nce_b`v|Vp5ig(H zJvRD^+ps46^hLX;=e2!2e;w9y1D@!D$c@Jc&%%%IL=+xzw55&2?darw=9g~>P z9>?Kdc$r?6c$m%x2S$sdpPl>GQZ{rC9mPS63*qjCVa?OIBj!fW zm|g?>CVfGXNjOfcyqImXR_(tXS(F{FcoNzKvG5R$IgGaxC@)i(e+$ME}vPVIhd|mx2IIE+f zM?9opQHIVgBWu)^A|RzXw!^??S!x)SZOwZaJkGjc<_}2l^eSBm!eAJG9T>EC6I_sy z?bxzDIAn&K5*mX)$RQzDA?s)-no-XF(g*yl4%+GBf`##bDXJ==AQk*xmnatI;SsLp zP9XTHq5mmS=iWu~9ES>b%Q=1aMa|ya^vj$@qz9S!ih{T8_PD%Sf_QrNKwgrXw9ldm zHRVR98*{C?_XNpJn{abA!oix_mowRMu^2lV-LPi;0+?-F(>^5#OHX-fPED zCu^l7u3E%STI}c4{J2!)9SUlGP_@!d?5W^QJXOI-Ea`hFMKjR7TluLvzC-ozCPn1`Tpy z!vlv@_Z58ILX6>nDjTp-1LlFMx~-%GA`aJvG$?8*Ihn;mH37eK**rmOEwqegf-Ccx zrIX4;{c~RK>XuTXxYo5kMiWMy)!IC{*DHG@E$hx?RwP@+wuad(P1{@%tRkyJRqD)3 zMHHHZ4boqDn>-=DgR5VlhQTpfVy182Gk;A_S8A1-;U1RR>+$62>(MUx@Nox$vTjHq z%QR=j!6Gdyb5wu7y(YUktwMuW5<@jl?m4cv4BODiT5o8qVdC0MBqGr@-YBIwnpZAY znX9(_uQjP}JJ=!~Ve9#5I~rUnN|P_3D$LqZcvBnywYhjlMSFHm`;u9GPla{5QD7(7*6Tb3Svr8;(nuAd81q$*uq6HC_&~je*Ca7hP4sJp0av{M8480wF zxASi7Qv+~@2U%Nu1Ud;s-G4CTVWIPyx!sg&8ZG0Wq zG_}i3C(6_1>q3w!EH7$Kwq8uBp2F2N7}l65mk1p*9v0&+;th=_E-W)E;w}P(j⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar b/2024-2025/FreshmanProjectBots/DontOverthinkIt/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar b/2024-2025/FreshmanProjectBots/Henrys_Command_Robot_Test/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 zcma&NWl&^owk(X(xVyW%ySuwf;qI=D6|RlDJ2cR^yEKh!@I- zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& zcxm3_e}n4{%|X zJp{G_j+%`j_q5}k{eW&TlP}J2wtZ2^<^E(O)4OQX8FDp6RJq!F{(6eHWSD3=f~(h} zJXCf7=r<16X{pHkm%yzYI_=VDP&9bmI1*)YXZeB}F? z(%QsB5fo*FUZxK$oX~X^69;x~j7ms8xlzpt-T15e9}$4T-pC z6PFg@;B-j|Ywajpe4~bk#S6(fO^|mm1hKOPfA%8-_iGCfICE|=P_~e;Wz6my&)h_~ zkv&_xSAw7AZ%ThYF(4jADW4vg=oEdJGVOs>FqamoL3Np8>?!W#!R-0%2Bg4h?kz5I zKV-rKN2n(vUL%D<4oj@|`eJ>0i#TmYBtYmfla;c!ATW%;xGQ0*TW@PTlGG><@dxUI zg>+3SiGdZ%?5N=8uoLA|$4isK$aJ%i{hECP$bK{J#0W2gQ3YEa zZQ50Stn6hqdfxJ*9#NuSLwKFCUGk@c=(igyVL;;2^wi4o30YXSIb2g_ud$ zgpCr@H0qWtk2hK8Q|&wx)}4+hTYlf;$a4#oUM=V@Cw#!$(nOFFpZ;0lc!qd=c$S}Z zGGI-0jg~S~cgVT=4Vo)b)|4phjStD49*EqC)IPwyeKBLcN;Wu@Aeph;emROAwJ-0< z_#>wVm$)ygH|qyxZaet&(Vf%pVdnvKWJn9`%DAxj3ot;v>S$I}jJ$FLBF*~iZ!ZXE zkvui&p}fI0Y=IDX)mm0@tAd|fEHl~J&K}ZX(Mm3cm1UAuwJ42+AO5@HwYfDH7ipIc zmI;1J;J@+aCNG1M`Btf>YT>~c&3j~Qi@Py5JT6;zjx$cvOQW@3oQ>|}GH?TW-E z1R;q^QFjm5W~7f}c3Ww|awg1BAJ^slEV~Pk`Kd`PS$7;SqJZNj->it4DW2l15}xP6 zoCl$kyEF%yJni0(L!Z&14m!1urXh6Btj_5JYt1{#+H8w?5QI%% zo-$KYWNMJVH?Hh@1n7OSu~QhSswL8x0=$<8QG_zepi_`y_79=nK=_ZP_`Em2UI*tyQoB+r{1QYZCpb?2OrgUw#oRH$?^Tj!Req>XiE#~B|~ z+%HB;=ic+R@px4Ld8mwpY;W^A%8%l8$@B@1m5n`TlKI6bz2mp*^^^1mK$COW$HOfp zUGTz-cN9?BGEp}5A!mDFjaiWa2_J2Iq8qj0mXzk; z66JBKRP{p%wN7XobR0YjhAuW9T1Gw3FDvR5dWJ8ElNYF94eF3ebu+QwKjtvVu4L zI9ip#mQ@4uqVdkl-TUQMb^XBJVLW(-$s;Nq;@5gr4`UfLgF$adIhd?rHOa%D);whv z=;krPp~@I+-Z|r#s3yCH+c1US?dnm+C*)r{m+86sTJusLdNu^sqLrfWed^ndHXH`m zd3#cOe3>w-ga(Dus_^ppG9AC>Iq{y%%CK+Cro_sqLCs{VLuK=dev>OL1dis4(PQ5R zcz)>DjEkfV+MO;~>VUlYF00SgfUo~@(&9$Iy2|G0T9BSP?&T22>K46D zL*~j#yJ?)^*%J3!16f)@Y2Z^kS*BzwfAQ7K96rFRIh>#$*$_Io;z>ux@}G98!fWR@ zGTFxv4r~v)Gsd|pF91*-eaZ3Qw1MH$K^7JhWIdX%o$2kCbvGDXy)a?@8T&1dY4`;L z4Kn+f%SSFWE_rpEpL9bnlmYq`D!6F%di<&Hh=+!VI~j)2mfil03T#jJ_s?}VV0_hp z7T9bWxc>Jm2Z0WMU?`Z$xE74Gu~%s{mW!d4uvKCx@WD+gPUQ zV0vQS(Ig++z=EHN)BR44*EDSWIyT~R4$FcF*VEY*8@l=218Q05D2$|fXKFhRgBIEE zdDFB}1dKkoO^7}{5crKX!p?dZWNz$m>1icsXG2N+((x0OIST9Zo^DW_tytvlwXGpn zs8?pJXjEG;T@qrZi%#h93?FP$!&P4JA(&H61tqQi=opRzNpm zkrG}$^t9&XduK*Qa1?355wd8G2CI6QEh@Ua>AsD;7oRUNLPb76m4HG3K?)wF~IyS3`fXuNM>${?wmB zpVz;?6_(Fiadfd{vUCBM*_kt$+F3J+IojI;9L(gc9n3{sEZyzR9o!_mOwFC#tQ{Q~ zP3-`#uK#tP3Q7~Q;4H|wjZHO8h7e4IuBxl&vz2w~D8)w=Wtg31zpZhz%+kzSzL*dV zwp@{WU4i;hJ7c2f1O;7Mz6qRKeASoIv0_bV=i@NMG*l<#+;INk-^`5w@}Dj~;k=|}qM1vq_P z|GpBGe_IKq|LNy9SJhKOQ$c=5L{Dv|Q_lZl=-ky*BFBJLW9&y_C|!vyM~rQx=!vun z?rZJQB5t}Dctmui5i31C_;_}CEn}_W%>oSXtt>@kE1=JW*4*v4tPp;O6 zmAk{)m!)}34pTWg8{i>($%NQ(Tl;QC@J@FfBoc%Gr&m560^kgSfodAFrIjF}aIw)X zoXZ`@IsMkc8_=w%-7`D6Y4e*CG8k%Ud=GXhsTR50jUnm+R*0A(O3UKFg0`K;qp1bl z7``HN=?39ic_kR|^R^~w-*pa?Vj#7|e9F1iRx{GN2?wK!xR1GW!qa=~pjJb-#u1K8 zeR?Y2i-pt}yJq;SCiVHODIvQJX|ZJaT8nO+(?HXbLefulKKgM^B(UIO1r+S=7;kLJ zcH}1J=Px2jsh3Tec&v8Jcbng8;V-`#*UHt?hB(pmOipKwf3Lz8rG$heEB30Sg*2rx zV<|KN86$soN(I!BwO`1n^^uF2*x&vJ$2d$>+`(romzHP|)K_KkO6Hc>_dwMW-M(#S zK(~SiXT1@fvc#U+?|?PniDRm01)f^#55;nhM|wi?oG>yBsa?~?^xTU|fX-R(sTA+5 zaq}-8Tx7zrOy#3*JLIIVsBmHYLdD}!0NP!+ITW+Thn0)8SS!$@)HXwB3tY!fMxc#1 zMp3H?q3eD?u&Njx4;KQ5G>32+GRp1Ee5qMO0lZjaRRu&{W<&~DoJNGkcYF<5(Ab+J zgO>VhBl{okDPn78<%&e2mR{jwVCz5Og;*Z;;3%VvoGo_;HaGLWYF7q#jDX=Z#Ml`H z858YVV$%J|e<1n`%6Vsvq7GmnAV0wW4$5qQ3uR@1i>tW{xrl|ExywIc?fNgYlA?C5 zh$ezAFb5{rQu6i7BSS5*J-|9DQ{6^BVQ{b*lq`xS@RyrsJN?-t=MTMPY;WYeKBCNg z^2|pN!Q^WPJuuO4!|P@jzt&tY1Y8d%FNK5xK(!@`jO2aEA*4 zkO6b|UVBipci?){-Ke=+1;mGlND8)6+P;8sq}UXw2hn;fc7nM>g}GSMWu&v&fqh

iViYT=fZ(|3Ox^$aWPp4a8h24tD<|8-!aK0lHgL$N7Efw}J zVIB!7=T$U`ao1?upi5V4Et*-lTG0XvExbf!ya{cua==$WJyVG(CmA6Of*8E@DSE%L z`V^$qz&RU$7G5mg;8;=#`@rRG`-uS18$0WPN@!v2d{H2sOqP|!(cQ@ zUHo!d>>yFArLPf1q`uBvY32miqShLT1B@gDL4XoVTK&@owOoD)OIHXrYK-a1d$B{v zF^}8D3Y^g%^cnvScOSJR5QNH+BI%d|;J;wWM3~l>${fb8DNPg)wrf|GBP8p%LNGN# z3EaIiItgwtGgT&iYCFy9-LG}bMI|4LdmmJt@V@% zb6B)1kc=T)(|L@0;wr<>=?r04N;E&ef+7C^`wPWtyQe(*pD1pI_&XHy|0gIGHMekd zF_*M4yi6J&Z4LQj65)S zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% z3}@9B=#JI3@B*#4s!O))~z zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g zhoV{7$q=*;=l{O>Q4a@ ziMjf_u*o^PsO)#BjC%0^h>Xp@;5$p{JSYDt)zbb}s{Kbt!T*I@Pk@X0zds6wsefuU zW$XY%yyRGC94=6mf?x+bbA5CDQ2AgW1T-jVAJbm7K(gp+;v6E0WI#kuACgV$r}6L? zd|Tj?^%^*N&b>Dd{Wr$FS2qI#Ucs1yd4N+RBUQiSZGujH`#I)mG&VKoDh=KKFl4=G z&MagXl6*<)$6P}*Tiebpz5L=oMaPrN+caUXRJ`D?=K9!e0f{@D&cZLKN?iNP@X0aF zE(^pl+;*T5qt?1jRC=5PMgV!XNITRLS_=9{CJExaQj;lt!&pdzpK?8p>%Mb+D z?yO*uSung=-`QQ@yX@Hyd4@CI^r{2oiu`%^bNkz+Nkk!IunjwNC|WcqvX~k=><-I3 zDQdbdb|!v+Iz01$w@aMl!R)koD77Xp;eZwzSl-AT zr@Vu{=xvgfq9akRrrM)}=!=xcs+U1JO}{t(avgz`6RqiiX<|hGG1pmop8k6Q+G_mv zJv|RfDheUp2L3=^C=4aCBMBn0aRCU(DQwX-W(RkRwmLeuJYF<0urcaf(=7)JPg<3P zQs!~G)9CT18o!J4{zX{_e}4eS)U-E)0FAt}wEI(c0%HkxgggW;(1E=>J17_hsH^sP z%lT0LGgbUXHx-K*CI-MCrP66UP0PvGqM$MkeLyqHdbgP|_Cm!7te~b8p+e6sQ_3k| zVcwTh6d83ltdnR>D^)BYQpDKlLk3g0Hdcgz2}%qUs9~~Rie)A-BV1mS&naYai#xcZ z(d{8=-LVpTp}2*y)|gR~;qc7fp26}lPcLZ#=JpYcn3AT9(UIdOyg+d(P5T7D&*P}# zQCYplZO5|7+r19%9e`v^vfSS1sbX1c%=w1;oyruXB%Kl$ACgKQ6=qNWLsc=28xJjg zwvsI5-%SGU|3p>&zXVl^vVtQT3o-#$UT9LI@Npz~6=4!>mc431VRNN8od&Ul^+G_kHC`G=6WVWM z%9eWNyy(FTO|A+@x}Ou3CH)oi;t#7rAxdIXfNFwOj_@Y&TGz6P_sqiB`Q6Lxy|Q{`|fgmRG(k+!#b*M+Z9zFce)f-7;?Km5O=LHV9f9_87; zF7%R2B+$?@sH&&-$@tzaPYkw0;=i|;vWdI|Wl3q_Zu>l;XdIw2FjV=;Mq5t1Q0|f< zs08j54Bp`3RzqE=2enlkZxmX6OF+@|2<)A^RNQpBd6o@OXl+i)zO%D4iGiQNuXd+zIR{_lb96{lc~bxsBveIw6umhShTX+3@ZJ=YHh@ zWY3(d0azg;7oHn>H<>?4@*RQbi>SmM=JrHvIG(~BrvI)#W(EAeO6fS+}mxxcc+X~W6&YVl86W9WFSS}Vz-f9vS?XUDBk)3TcF z8V?$4Q)`uKFq>xT=)Y9mMFVTUk*NIA!0$?RP6Ig0TBmUFrq*Q-Agq~DzxjStQyJ({ zBeZ;o5qUUKg=4Hypm|}>>L=XKsZ!F$yNTDO)jt4H0gdQ5$f|d&bnVCMMXhNh)~mN z@_UV6D7MVlsWz+zM+inZZp&P4fj=tm6fX)SG5H>OsQf_I8c~uGCig$GzuwViK54bcgL;VN|FnyQl>Ed7(@>=8$a_UKIz|V6CeVSd2(P z0Uu>A8A+muM%HLFJQ9UZ5c)BSAv_zH#1f02x?h9C}@pN@6{>UiAp>({Fn(T9Q8B z^`zB;kJ5b`>%dLm+Ol}ty!3;8f1XDSVX0AUe5P#@I+FQ-`$(a;zNgz)4x5hz$Hfbg z!Q(z26wHLXko(1`;(BAOg_wShpX0ixfWq3ponndY+u%1gyX)_h=v1zR#V}#q{au6; z!3K=7fQwnRfg6FXtNQmP>`<;!N137paFS%y?;lb1@BEdbvQHYC{976l`cLqn;b8lp zIDY>~m{gDj(wfnK!lpW6pli)HyLEiUrNc%eXTil|F2s(AY+LW5hkKb>TQ3|Q4S9rr zpDs4uK_co6XPsn_z$LeS{K4jFF`2>U`tbgKdyDne`xmR<@6AA+_hPNKCOR-Zqv;xk zu5!HsBUb^!4uJ7v0RuH-7?l?}b=w5lzzXJ~gZcxRKOovSk@|#V+MuX%Y+=;14i*%{)_gSW9(#4%)AV#3__kac1|qUy!uyP{>?U#5wYNq}y$S9pCc zFc~4mgSC*G~j0u#qqp9 z${>3HV~@->GqEhr_Xwoxq?Hjn#=s2;i~g^&Hn|aDKpA>Oc%HlW(KA1?BXqpxB;Ydx)w;2z^MpjJ(Qi(X!$5RC z*P{~%JGDQqojV>2JbEeCE*OEu!$XJ>bWA9Oa_Hd;y)F%MhBRi*LPcdqR8X`NQ&1L# z5#9L*@qxrx8n}LfeB^J{%-?SU{FCwiWyHp682F+|pa+CQa3ZLzBqN1{)h4d6+vBbV zC#NEbQLC;}me3eeYnOG*nXOJZEU$xLZ1<1Y=7r0(-U0P6-AqwMAM`a(Ed#7vJkn6plb4eI4?2y3yOTGmmDQ!z9`wzbf z_OY#0@5=bnep;MV0X_;;SJJWEf^E6Bd^tVJ9znWx&Ks8t*B>AM@?;D4oWUGc z!H*`6d7Cxo6VuyS4Eye&L1ZRhrRmN6Lr`{NL(wDbif|y&z)JN>Fl5#Wi&mMIr5i;x zBx}3YfF>>8EC(fYnmpu~)CYHuHCyr5*`ECap%t@y=jD>!_%3iiE|LN$mK9>- zHdtpy8fGZtkZF?%TW~29JIAfi2jZT8>OA7=h;8T{{k?c2`nCEx9$r zS+*&vt~2o^^J+}RDG@+9&M^K*z4p{5#IEVbz`1%`m5c2};aGt=V?~vIM}ZdPECDI)47|CWBCfDWUbxBCnmYivQ*0Nu_xb*C>~C9(VjHM zxe<*D<#dQ8TlpMX2c@M<9$w!RP$hpG4cs%AI){jp*Sj|*`m)5(Bw*A0$*i-(CA5#%>a)$+jI2C9r6|(>J8InryENI z$NohnxDUB;wAYDwrb*!N3noBTKPpPN}~09SEL18tkG zxgz(RYU_;DPT{l?Q$+eaZaxnsWCA^ds^0PVRkIM%bOd|G2IEBBiz{&^JtNsODs;5z zICt_Zj8wo^KT$7Bg4H+y!Df#3mbl%%?|EXe!&(Vmac1DJ*y~3+kRKAD=Ovde4^^%~ zw<9av18HLyrf*_>Slp;^i`Uy~`mvBjZ|?Ad63yQa#YK`4+c6;pW4?XIY9G1(Xh9WO8{F-Aju+nS9Vmv=$Ac0ienZ+p9*O%NG zMZKy5?%Z6TAJTE?o5vEr0r>f>hb#2w2U3DL64*au_@P!J!TL`oH2r*{>ffu6|A7tv zL4juf$DZ1MW5ZPsG!5)`k8d8c$J$o;%EIL0va9&GzWvkS%ZsGb#S(?{!UFOZ9<$a| zY|a+5kmD5N&{vRqkgY>aHsBT&`rg|&kezoD)gP0fsNYHsO#TRc_$n6Lf1Z{?+DLziXlHrq4sf(!>O{?Tj;Eh@%)+nRE_2VxbN&&%%caU#JDU%vL3}Cb zsb4AazPI{>8H&d=jUaZDS$-0^AxE@utGs;-Ez_F(qC9T=UZX=>ok2k2 ziTn{K?y~a5reD2A)P${NoI^>JXn>`IeArow(41c-Wm~)wiryEP(OS{YXWi7;%dG9v zI?mwu1MxD{yp_rrk!j^cKM)dc4@p4Ezyo%lRN|XyD}}>v=Xoib0gOcdXrQ^*61HNj z=NP|pd>@yfvr-=m{8$3A8TQGMTE7g=z!%yt`8`Bk-0MMwW~h^++;qyUP!J~ykh1GO z(FZ59xuFR$(WE;F@UUyE@Sp>`aVNjyj=Ty>_Vo}xf`e7`F;j-IgL5`1~-#70$9_=uBMq!2&1l zomRgpD58@)YYfvLtPW}{C5B35R;ZVvB<<#)x%srmc_S=A7F@DW8>QOEGwD6suhwCg z>Pa+YyULhmw%BA*4yjDp|2{!T98~<6Yfd(wo1mQ!KWwq0eg+6)o1>W~f~kL<-S+P@$wx*zeI|1t7z#Sxr5 zt6w+;YblPQNplq4Z#T$GLX#j6yldXAqj>4gAnnWtBICUnA&-dtnlh=t0Ho_vEKwV` z)DlJi#!@nkYV#$!)@>udAU*hF?V`2$Hf=V&6PP_|r#Iv*J$9)pF@X3`k;5})9^o4y z&)~?EjX5yX12O(BsFy-l6}nYeuKkiq`u9145&3Ssg^y{5G3Pse z9w(YVa0)N-fLaBq1`P!_#>SS(8fh_5!f{UrgZ~uEdeMJIz7DzI5!NHHqQtm~#CPij z?=N|J>nPR6_sL7!f4hD_|KH`vf8(Wpnj-(gPWH+ZvID}%?~68SwhPTC3u1_cB`otq z)U?6qo!ZLi5b>*KnYHWW=3F!p%h1;h{L&(Q&{qY6)_qxNfbP6E3yYpW!EO+IW3?@J z);4>g4gnl^8klu7uA>eGF6rIGSynacogr)KUwE_R4E5Xzi*Qir@b-jy55-JPC8c~( zo!W8y9OGZ&`xmc8;=4-U9=h{vCqfCNzYirONmGbRQlR`WWlgnY+1wCXbMz&NT~9*| z6@FrzP!LX&{no2!Ln_3|I==_4`@}V?4a;YZKTdw;vT<+K+z=uWbW(&bXEaWJ^W8Td z-3&1bY^Z*oM<=M}LVt>_j+p=2Iu7pZmbXrhQ_k)ysE9yXKygFNw$5hwDn(M>H+e1&9BM5!|81vd%r%vEm zqxY3?F@fb6O#5UunwgAHR9jp_W2zZ}NGp2%mTW@(hz7$^+a`A?mb8|_G*GNMJ) zjqegXQio=i@AINre&%ofexAr95aop5C+0MZ0m-l=MeO8m3epm7U%vZB8+I+C*iNFM z#T3l`gknX;D$-`2XT^Cg*vrv=RH+P;_dfF++cP?B_msQI4j+lt&rX2)3GaJx%W*Nn zkML%D{z5tpHH=dksQ*gzc|}gzW;lwAbxoR07VNgS*-c3d&8J|;@3t^ zVUz*J*&r7DFRuFVDCJDK8V9NN5hvpgGjwx+5n)qa;YCKe8TKtdnh{I7NU9BCN!0dq zczrBk8pE{{@vJa9ywR@mq*J=v+PG;?fwqlJVhijG!3VmIKs>9T6r7MJpC)m!Tc#>g zMtVsU>wbwFJEfwZ{vB|ZlttNe83)$iz`~#8UJ^r)lJ@HA&G#}W&ZH*;k{=TavpjWE z7hdyLZPf*X%Gm}i`Y{OGeeu^~nB8=`{r#TUrM-`;1cBvEd#d!kPqIgYySYhN-*1;L z^byj%Yi}Gx)Wnkosi337BKs}+5H5dth1JA{Ir-JKN$7zC)*}hqeoD(WfaUDPT>0`- z(6sa0AoIqASwF`>hP}^|)a_j2s^PQn*qVC{Q}htR z5-)duBFXT_V56-+UohKXlq~^6uf!6sA#ttk1o~*QEy_Y-S$gAvq47J9Vtk$5oA$Ct zYhYJ@8{hsC^98${!#Ho?4y5MCa7iGnfz}b9jE~h%EAAv~Qxu)_rAV;^cygV~5r_~?l=B`zObj7S=H=~$W zPtI_m%g$`kL_fVUk9J@>EiBH zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j zj9@UBW+N|4HW4AWapy4wfUI- zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce z-2EIl?~s z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S z0r71`WmAvJJ`1h&poLftLUS6Ir zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X z-_RGG@wt|%u`XUc%W{J z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB zYOjIisi+igK0#yW*gBGj?@I{~mUOvRFQR^pJbEbzFxTubnrw(Muk%}jI+vXmJ;{Q6 zrSobKD>T%}jV4Ub?L1+MGOD~0Ir%-`iTnWZN^~YPrcP5y3VMAzQ+&en^VzKEb$K!Q z<7Dbg&DNXuow*eD5yMr+#08nF!;%4vGrJI++5HdCFcGLfMW!KS*Oi@=7hFwDG!h2< zPunUEAF+HncQkbfFj&pbzp|MU*~60Z(|Ik%Tn{BXMN!hZOosNIseT?R;A`W?=d?5X zK(FB=9mZusYahp|K-wyb={rOpdn=@;4YI2W0EcbMKyo~-#^?h`BA9~o285%oY zfifCh5Lk$SY@|2A@a!T2V+{^!psQkx4?x0HSV`(w9{l75QxMk!)U52Lbhn{8ol?S) zCKo*7R(z!uk<6*qO=wh!Pul{(qq6g6xW;X68GI_CXp`XwO zxuSgPRAtM8K7}5E#-GM!*ydOOG_{A{)hkCII<|2=ma*71ci_-}VPARm3crFQjLYV! z9zbz82$|l01mv`$WahE2$=fAGWkd^X2kY(J7iz}WGS z@%MyBEO=A?HB9=^?nX`@nh;7;laAjs+fbo!|K^mE!tOB>$2a_O0y-*uaIn8k^6Y zSbuv;5~##*4Y~+y7Z5O*3w4qgI5V^17u*ZeupVGH^nM&$qmAk|anf*>r zWc5CV;-JY-Z@Uq1Irpb^O`L_7AGiqd*YpGUShb==os$uN3yYvb`wm6d=?T*it&pDk zo`vhw)RZX|91^^Wa_ti2zBFyWy4cJu#g)_S6~jT}CC{DJ_kKpT`$oAL%b^!2M;JgT zM3ZNbUB?}kP(*YYvXDIH8^7LUxz5oE%kMhF!rnPqv!GiY0o}NR$OD=ITDo9r%4E>E0Y^R(rS^~XjWyVI6 zMOR5rPXhTp*G*M&X#NTL`Hu*R+u*QNoiOKg4CtNPrjgH>c?Hi4MUG#I917fx**+pJfOo!zFM&*da&G_x)L(`k&TPI*t3e^{crd zX<4I$5nBQ8Ax_lmNRa~E*zS-R0sxkz`|>7q_?*e%7bxqNm3_eRG#1ae3gtV9!fQpY z+!^a38o4ZGy9!J5sylDxZTx$JmG!wg7;>&5H1)>f4dXj;B+@6tMlL=)cLl={jLMxY zbbf1ax3S4>bwB9-$;SN2?+GULu;UA-35;VY*^9Blx)Jwyb$=U!D>HhB&=jSsd^6yw zL)?a|>GxU!W}ocTC(?-%z3!IUhw^uzc`Vz_g>-tv)(XA#JK^)ZnC|l1`@CdX1@|!| z_9gQ)7uOf?cR@KDp97*>6X|;t@Y`k_N@)aH7gY27)COv^P3ya9I{4z~vUjLR9~z1Z z5=G{mVtKH*&$*t0@}-i_v|3B$AHHYale7>E+jP`ClqG%L{u;*ff_h@)al?RuL7tOO z->;I}>%WI{;vbLP3VIQ^iA$4wl6@0sDj|~112Y4OFjMs`13!$JGkp%b&E8QzJw_L5 zOnw9joc0^;O%OpF$Qp)W1HI!$4BaXX84`%@#^dk^hFp^pQ@rx4g(8Xjy#!X%+X5Jd@fs3amGT`}mhq#L97R>OwT5-m|h#yT_-v@(k$q7P*9X~T*3)LTdzP!*B} z+SldbVWrrwQo9wX*%FyK+sRXTa@O?WM^FGWOE?S`R(0P{<6p#f?0NJvnBia?k^fX2 zNQs7K-?EijgHJY}&zsr;qJ<*PCZUd*x|dD=IQPUK_nn)@X4KWtqoJNHkT?ZWL_hF? zS8lp2(q>;RXR|F;1O}EE#}gCrY~#n^O`_I&?&z5~7N;zL0)3Tup`%)oHMK-^r$NT% zbFg|o?b9w(q@)6w5V%si<$!U<#}s#x@0aX-hP>zwS#9*75VXA4K*%gUc>+yzupTDBOKH8WR4V0pM(HrfbQ&eJ79>HdCvE=F z|J>s;;iDLB^3(9}?biKbxf1$lI!*Z%*0&8UUq}wMyPs_hclyQQi4;NUY+x2qy|0J; zhn8;5)4ED1oHwg+VZF|80<4MrL97tGGXc5Sw$wAI#|2*cvQ=jB5+{AjMiDHmhUC*a zlmiZ`LAuAn_}hftXh;`Kq0zblDk8?O-`tnilIh|;3lZp@F_osJUV9`*R29M?7H{Fy z`nfVEIDIWXmU&YW;NjU8)EJpXhxe5t+scf|VXM!^bBlwNh)~7|3?fWwo_~ZFk(22% zTMesYw+LNx3J-_|DM~`v93yXe=jPD{q;li;5PD?Dyk+b? zo21|XpT@)$BM$%F=P9J19Vi&1#{jM3!^Y&fr&_`toi`XB1!n>sbL%U9I5<7!@?t)~ z;&H%z>bAaQ4f$wIzkjH70;<8tpUoxzKrPhn#IQfS%9l5=Iu))^XC<58D!-O z{B+o5R^Z21H0T9JQ5gNJnqh#qH^na|z92=hONIM~@_iuOi|F>jBh-?aA20}Qx~EpDGElELNn~|7WRXRFnw+Wdo`|# zBpU=Cz3z%cUJ0mx_1($X<40XEIYz(`noWeO+x#yb_pwj6)R(__%@_Cf>txOQ74wSJ z0#F3(zWWaR-jMEY$7C*3HJrohc79>MCUu26mfYN)f4M~4gD`}EX4e}A!U}QV8!S47 z6y-U-%+h`1n`*pQuKE%Av0@)+wBZr9mH}@vH@i{v(m-6QK7Ncf17x_D=)32`FOjjo zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# zKN+ zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ zbAf2yDNe0q}NEUvq_Quq3cTjcw z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N z+B2FcqvI9>jGtnK%eO%y zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G z22^iGhV@uaJh(XyyY%} zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ z@<_2VANlYF$vIH$ zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> zZG`aGq|yTNjv;9E+Txuoa@A(9V7g?1_T5FzRI;!=NP1Kqou1z5?%X~Wwb{trRfd>i z8&y^H)8YnKyA_Fyx>}RNmQIczT?w2J4SNvI{5J&}Wto|8FR(W;Qw#b1G<1%#tmYzQ zQ2mZA-PAdi%RQOhkHy9Ea#TPSw?WxwL@H@cbkZwIq0B!@ns}niALidmn&W?!Vd4Gj zO7FiuV4*6Mr^2xlFSvM;Cp_#r8UaqIzHJQg_z^rEJw&OMm_8NGAY2)rKvki|o1bH~ z$2IbfVeY2L(^*rMRU1lM5Y_sgrDS`Z??nR2lX;zyR=c%UyGb*%TC-Dil?SihkjrQy~TMv6;BMs7P8il`H7DmpVm@rJ;b)hW)BL)GjS154b*xq-NXq2cwE z^;VP7ua2pxvCmxrnqUYQMH%a%nHmwmI33nJM(>4LznvY*k&C0{8f*%?zggpDgkuz&JBx{9mfb@wegEl2v!=}Sq2Gaty0<)UrOT0{MZtZ~j5y&w zXlYa_jY)I_+VA-^#mEox#+G>UgvM!Ac8zI<%JRXM_73Q!#i3O|)lOP*qBeJG#BST0 zqohi)O!|$|2SeJQo(w6w7%*92S})XfnhrH_Z8qe!G5>CglP=nI7JAOW?(Z29;pXJ9 zR9`KzQ=WEhy*)WH>$;7Cdz|>*i>=##0bB)oU0OR>>N<21e4rMCHDemNi2LD>Nc$;& zQRFthpWniC1J6@Zh~iJCoLOxN`oCKD5Q4r%ynwgUKPlIEd#?QViIqovY|czyK8>6B zSP%{2-<;%;1`#0mG^B(8KbtXF;Nf>K#Di72UWE4gQ%(_26Koiad)q$xRL~?pN71ZZ zujaaCx~jXjygw;rI!WB=xrOJO6HJ!!w}7eiivtCg5K|F6$EXa)=xUC za^JXSX98W`7g-tm@uo|BKj39Dl;sg5ta;4qjo^pCh~{-HdLl6qI9Ix6f$+qiZ$}s= zNguKrU;u+T@ko(Vr1>)Q%h$?UKXCY>3se%&;h2osl2D zE4A9bd7_|^njDd)6cI*FupHpE3){4NQ*$k*cOWZ_?CZ>Z4_fl@n(mMnYK62Q1d@+I zr&O))G4hMihgBqRIAJkLdk(p(D~X{-oBUA+If@B}j& zsHbeJ3RzTq96lB7d($h$xTeZ^gP0c{t!Y0c)aQE;$FY2!mACg!GDEMKXFOPI^)nHZ z`aSPJpvV0|bbrzhWWkuPURlDeN%VT8tndV8?d)eN*i4I@u zVKl^6{?}A?P)Fsy?3oi#clf}L18t;TjNI2>eI&(ezDK7RyqFxcv%>?oxUlonv(px) z$vnPzRH`y5A(x!yOIfL0bmgeMQB$H5wenx~!ujQK*nUBW;@Em&6Xv2%s(~H5WcU2R z;%Nw<$tI)a`Ve!>x+qegJnQsN2N7HaKzrFqM>`6R*gvh%O*-%THt zrB$Nk;lE;z{s{r^PPm5qz(&lM{sO*g+W{sK+m3M_z=4=&CC>T`{X}1Vg2PEfSj2x_ zmT*(x;ov%3F?qoEeeM>dUn$a*?SIGyO8m806J1W1o+4HRhc2`9$s6hM#qAm zChQ87b~GEw{ADfs+5}FJ8+|bIlIv(jT$Ap#hSHoXdd9#w<#cA<1Rkq^*EEkknUd4& zoIWIY)sAswy6fSERVm&!SO~#iN$OgOX*{9@_BWFyJTvC%S++ilSfCrO(?u=Dc?CXZ zzCG&0yVR{Z`|ZF0eEApWEo#s9osV>F{uK{QA@BES#&;#KsScf>y zvs?vIbI>VrT<*!;XmQS=bhq%46-aambZ(8KU-wOO2=en~D}MCToB_u;Yz{)1ySrPZ z@=$}EvjTdzTWU7c0ZI6L8=yP+YRD_eMMos}b5vY^S*~VZysrkq<`cK3>>v%uy7jgq z0ilW9KjVDHLv0b<1K_`1IkbTOINs0=m-22c%M~l=^S}%hbli-3?BnNq?b`hx^HX2J zIe6ECljRL0uBWb`%{EA=%!i^4sMcj+U_TaTZRb+~GOk z^ZW!nky0n*Wb*r+Q|9H@ml@Z5gU&W`(z4-j!OzC1wOke`TRAYGZVl$PmQ16{3196( zO*?`--I}Qf(2HIwb2&1FB^!faPA2=sLg(@6P4mN)>Dc3i(B0;@O-y2;lM4akD>@^v z=u>*|!s&9zem70g7zfw9FXl1bpJW(C#5w#uy5!V?Q(U35A~$dR%LDVnq@}kQm13{} zd53q3N(s$Eu{R}k2esbftfjfOITCL;jWa$}(mmm}d(&7JZ6d3%IABCapFFYjdEjdK z&4Edqf$G^MNAtL=uCDRs&Fu@FXRgX{*0<(@c3|PNHa>L%zvxWS={L8%qw`STm+=Rd zA}FLspESSIpE_^41~#5yI2bJ=9`oc;GIL!JuW&7YetZ?0H}$$%8rW@*J37L-~Rsx!)8($nI4 zZhcZ2^=Y+p4YPl%j!nFJA|*M^gc(0o$i3nlphe+~-_m}jVkRN{spFs(o0ajW@f3K{ zDV!#BwL322CET$}Y}^0ixYj2w>&Xh12|R8&yEw|wLDvF!lZ#dOTHM9pK6@Nm-@9Lnng4ZHBgBSrr7KI8YCC9DX5Kg|`HsiwJHg2(7#nS;A{b3tVO?Z% za{m5b3rFV6EpX;=;n#wltDv1LE*|g5pQ+OY&*6qCJZc5oDS6Z6JD#6F)bWxZSF@q% z+1WV;m!lRB!n^PC>RgQCI#D1br_o^#iPk>;K2hB~0^<~)?p}LG%kigm@moD#q3PE+ zA^Qca)(xnqw6x>XFhV6ku9r$E>bWNrVH9fum0?4s?Rn2LG{Vm_+QJHse6xa%nzQ?k zKug4PW~#Gtb;#5+9!QBgyB@q=sk9=$S{4T>wjFICStOM?__fr+Kei1 z3j~xPqW;W@YkiUM;HngG!;>@AITg}vAE`M2Pj9Irl4w1fo4w<|Bu!%rh%a(Ai^Zhi zs92>v5;@Y(Zi#RI*ua*h`d_7;byQSa*v9E{2x$<-_=5Z<7{%)}4XExANcz@rK69T0x3%H<@frW>RA8^swA+^a(FxK| zFl3LD*ImHN=XDUkrRhp6RY5$rQ{bRgSO*(vEHYV)3Mo6Jy3puiLmU&g82p{qr0F?ohmbz)f2r{X2|T2 z$4fdQ=>0BeKbiVM!e-lIIs8wVTuC_m7}y4A_%ikI;Wm5$9j(^Y z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} z3mS%$2Be7{l(+MVx3 z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! diff --git a/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar b/2024-2025/Tests/main-bot/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar b/2024-2025/Win-Nguyen/Win-Nguyen-2024/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/Example_Project/gradle/wrapper/gradle-wrapper.jar b/Example_Project/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e5832f090a2944b7473328c07c9755baa3196..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60756 zcmb5WV{~QRw(p$^Dz@00IL3?^hro$gg*4VI_WAaTyVM5Foj~O|-84 z$;06hMwt*rV;^8iB z1~&0XWpYJmG?Ts^K9PC62H*`G}xom%S%yq|xvG~FIfP=9*f zZoDRJBm*Y0aId=qJ?7dyb)6)JGWGwe)MHeNSzhi)Ko6J<-m@v=a%NsP537lHe0R* z`If4$aaBA#S=w!2z&m>{lpTy^Lm^mg*3?M&7HFv}7K6x*cukLIGX;bQG|QWdn{%_6 zHnwBKr84#B7Z+AnBXa16a?or^R?+>$4`}{*a_>IhbjvyTtWkHw)|ay)ahWUd-qq$~ zMbh6roVsj;_qnC-R{G+Cy6bApVOinSU-;(DxUEl!i2)1EeQ9`hrfqj(nKI7?Z>Xur zoJz-a`PxkYit1HEbv|jy%~DO^13J-ut986EEG=66S}D3!L}Efp;Bez~7tNq{QsUMm zh9~(HYg1pA*=37C0}n4g&bFbQ+?-h-W}onYeE{q;cIy%eZK9wZjSwGvT+&Cgv z?~{9p(;bY_1+k|wkt_|N!@J~aoY@|U_RGoWX<;p{Nu*D*&_phw`8jYkMNpRTWx1H* z>J-Mi_!`M468#5Aix$$u1M@rJEIOc?k^QBc?T(#=n&*5eS#u*Y)?L8Ha$9wRWdH^3D4|Ps)Y?m0q~SiKiSfEkJ!=^`lJ(%W3o|CZ zSrZL-Xxc{OrmsQD&s~zPfNJOpSZUl%V8tdG%ei}lQkM+z@-4etFPR>GOH9+Y_F<3=~SXln9Kb-o~f>2a6Xz@AS3cn^;c_>lUwlK(n>z?A>NbC z`Ud8^aQy>wy=$)w;JZzA)_*Y$Z5hU=KAG&htLw1Uh00yE!|Nu{EZkch zY9O6x7Y??>!7pUNME*d!=R#s)ghr|R#41l!c?~=3CS8&zr6*aA7n9*)*PWBV2w+&I zpW1-9fr3j{VTcls1>ua}F*bbju_Xq%^v;-W~paSqlf zolj*dt`BBjHI)H9{zrkBo=B%>8}4jeBO~kWqO!~Thi!I1H(in=n^fS%nuL=X2+s!p}HfTU#NBGiwEBF^^tKU zbhhv+0dE-sbK$>J#t-J!B$TMgN@Wh5wTtK2BG}4BGfsZOoRUS#G8Cxv|6EI*n&Xxq zt{&OxCC+BNqz$9b0WM7_PyBJEVObHFh%%`~!@MNZlo*oXDCwDcFwT~Rls!aApL<)^ zbBftGKKBRhB!{?fX@l2_y~%ygNFfF(XJzHh#?`WlSL{1lKT*gJM zs>bd^H9NCxqxn(IOky5k-wALFowQr(gw%|`0991u#9jXQh?4l|l>pd6a&rx|v=fPJ z1mutj{YzpJ_gsClbWFk(G}bSlFi-6@mwoQh-XeD*j@~huW4(8ub%^I|azA)h2t#yG z7e_V_<4jlM3D(I+qX}yEtqj)cpzN*oCdYHa!nm%0t^wHm)EmFP*|FMw!tb@&`G-u~ zK)=Sf6z+BiTAI}}i{*_Ac$ffr*Wrv$F7_0gJkjx;@)XjYSh`RjAgrCck`x!zP>Ifu z&%he4P|S)H*(9oB4uvH67^0}I-_ye_!w)u3v2+EY>eD3#8QR24<;7?*hj8k~rS)~7 zSXs5ww)T(0eHSp$hEIBnW|Iun<_i`}VE0Nc$|-R}wlSIs5pV{g_Dar(Zz<4X3`W?K z6&CAIl4U(Qk-tTcK{|zYF6QG5ArrEB!;5s?tW7 zrE3hcFY&k)+)e{+YOJ0X2uDE_hd2{|m_dC}kgEKqiE9Q^A-+>2UonB+L@v3$9?AYw zVQv?X*pK;X4Ovc6Ev5Gbg{{Eu*7{N3#0@9oMI~}KnObQE#Y{&3mM4`w%wN+xrKYgD zB-ay0Q}m{QI;iY`s1Z^NqIkjrTlf`B)B#MajZ#9u41oRBC1oM1vq0i|F59> z#StM@bHt|#`2)cpl_rWB($DNJ3Lap}QM-+A$3pe}NyP(@+i1>o^fe-oxX#Bt`mcQc zb?pD4W%#ep|3%CHAYnr*^M6Czg>~L4?l16H1OozM{P*en298b+`i4$|w$|4AHbzqB zHpYUsHZET$Z0ztC;U+0*+amF!@PI%^oUIZy{`L{%O^i{Xk}X0&nl)n~tVEpcAJSJ} zverw15zP1P-O8h9nd!&hj$zuwjg?DoxYIw{jWM zW5_pj+wFy8Tsa9g<7Qa21WaV&;ejoYflRKcz?#fSH_)@*QVlN2l4(QNk| z4aPnv&mrS&0|6NHq05XQw$J^RR9T{3SOcMKCXIR1iSf+xJ0E_Wv?jEc*I#ZPzyJN2 zUG0UOXHl+PikM*&g$U@g+KbG-RY>uaIl&DEtw_Q=FYq?etc!;hEC_}UX{eyh%dw2V zTTSlap&5>PY{6I#(6`j-9`D&I#|YPP8a;(sOzgeKDWsLa!i-$frD>zr-oid!Hf&yS z!i^cr&7tN}OOGmX2)`8k?Tn!!4=tz~3hCTq_9CdiV!NIblUDxHh(FJ$zs)B2(t5@u z-`^RA1ShrLCkg0)OhfoM;4Z{&oZmAec$qV@ zGQ(7(!CBk<5;Ar%DLJ0p0!ResC#U<+3i<|vib1?{5gCebG7$F7URKZXuX-2WgF>YJ^i zMhHDBsh9PDU8dlZ$yJKtc6JA#y!y$57%sE>4Nt+wF1lfNIWyA`=hF=9Gj%sRwi@vd z%2eVV3y&dvAgyuJ=eNJR+*080dbO_t@BFJO<@&#yqTK&+xc|FRR;p;KVk@J3$S{p` zGaMj6isho#%m)?pOG^G0mzOAw0z?!AEMsv=0T>WWcE>??WS=fII$t$(^PDPMU(P>o z_*0s^W#|x)%tx8jIgZY~A2yG;US0m2ZOQt6yJqW@XNY_>_R7(Nxb8Ged6BdYW6{prd!|zuX$@Q2o6Ona8zzYC1u!+2!Y$Jc9a;wy+pXt}o6~Bu1oF1c zp7Y|SBTNi@=I(K%A60PMjM#sfH$y*c{xUgeSpi#HB`?|`!Tb&-qJ3;vxS!TIzuTZs-&%#bAkAyw9m4PJgvey zM5?up*b}eDEY+#@tKec)-c(#QF0P?MRlD1+7%Yk*jW;)`f;0a-ZJ6CQA?E%>i2Dt7T9?s|9ZF|KP4;CNWvaVKZ+Qeut;Jith_y{v*Ny6Co6!8MZx;Wgo z=qAi%&S;8J{iyD&>3CLCQdTX*$+Rx1AwA*D_J^0>suTgBMBb=*hefV+Ars#mmr+YsI3#!F@Xc1t4F-gB@6aoyT+5O(qMz*zG<9Qq*f0w^V!03rpr*-WLH}; zfM{xSPJeu6D(%8HU%0GEa%waFHE$G?FH^kMS-&I3)ycx|iv{T6Wx}9$$D&6{%1N_8 z_CLw)_9+O4&u94##vI9b-HHm_95m)fa??q07`DniVjAy`t7;)4NpeyAY(aAk(+T_O z1om+b5K2g_B&b2DCTK<>SE$Ode1DopAi)xaJjU>**AJK3hZrnhEQ9E`2=|HHe<^tv z63e(bn#fMWuz>4erc47}!J>U58%<&N<6AOAewyzNTqi7hJc|X{782&cM zHZYclNbBwU6673=!ClmxMfkC$(CykGR@10F!zN1Se83LR&a~$Ht&>~43OX22mt7tcZUpa;9@q}KDX3O&Ugp6< zLZLfIMO5;pTee1vNyVC$FGxzK2f>0Z-6hM82zKg44nWo|n}$Zk6&;5ry3`(JFEX$q zK&KivAe${e^5ZGc3a9hOt|!UOE&OocpVryE$Y4sPcs4rJ>>Kbi2_subQ9($2VN(3o zb~tEzMsHaBmBtaHAyES+d3A(qURgiskSSwUc9CfJ@99&MKp2sooSYZu+-0t0+L*!I zYagjOlPgx|lep9tiU%ts&McF6b0VE57%E0Ho%2oi?=Ks+5%aj#au^OBwNwhec zta6QAeQI^V!dF1C)>RHAmB`HnxyqWx?td@4sd15zPd*Fc9hpDXP23kbBenBxGeD$k z;%0VBQEJ-C)&dTAw_yW@k0u?IUk*NrkJ)(XEeI z9Y>6Vel>#s_v@=@0<{4A{pl=9cQ&Iah0iD0H`q)7NeCIRz8zx;! z^OO;1+IqoQNak&pV`qKW+K0^Hqp!~gSohcyS)?^P`JNZXw@gc6{A3OLZ?@1Uc^I2v z+X!^R*HCm3{7JPq{8*Tn>5;B|X7n4QQ0Bs79uTU%nbqOJh`nX(BVj!#f;#J+WZxx4 z_yM&1Y`2XzhfqkIMO7tB3raJKQS+H5F%o83bM+hxbQ zeeJm=Dvix$2j|b4?mDacb67v-1^lTp${z=jc1=j~QD>7c*@+1?py>%Kj%Ejp7Y-!? z8iYRUlGVrQPandAaxFfks53@2EC#0)%mrnmGRn&>=$H$S8q|kE_iWko4`^vCS2aWg z#!`RHUGyOt*k?bBYu3*j3u0gB#v(3tsije zgIuNNWNtrOkx@Pzs;A9un+2LX!zw+p3_NX^Sh09HZAf>m8l@O*rXy_82aWT$Q>iyy zqO7Of)D=wcSn!0+467&!Hl))eff=$aneB?R!YykdKW@k^_uR!+Q1tR)+IJb`-6=jj zymzA>Sv4>Z&g&WWu#|~GcP7qP&m*w-S$)7Xr;(duqCTe7p8H3k5>Y-n8438+%^9~K z3r^LIT_K{i7DgEJjIocw_6d0!<;wKT`X;&vv+&msmhAAnIe!OTdybPctzcEzBy88_ zWO{6i4YT%e4^WQZB)KHCvA(0tS zHu_Bg+6Ko%a9~$EjRB90`P(2~6uI@SFibxct{H#o&y40MdiXblu@VFXbhz>Nko;7R z70Ntmm-FePqhb%9gL+7U8@(ch|JfH5Fm)5${8|`Lef>LttM_iww6LW2X61ldBmG0z zax3y)njFe>j*T{i0s8D4=L>X^j0)({R5lMGVS#7(2C9@AxL&C-lZQx~czI7Iv+{%1 z2hEG>RzX4S8x3v#9sgGAnPzptM)g&LB}@%E>fy0vGSa(&q0ch|=ncKjNrK z`jA~jObJhrJ^ri|-)J^HUyeZXz~XkBp$VhcTEcTdc#a2EUOGVX?@mYx#Vy*!qO$Jv zQ4rgOJ~M*o-_Wptam=~krnmG*p^j!JAqoQ%+YsDFW7Cc9M%YPiBOrVcD^RY>m9Pd< zu}#9M?K{+;UIO!D9qOpq9yxUquQRmQNMo0pT`@$pVt=rMvyX)ph(-CCJLvUJy71DI zBk7oc7)-%ngdj~s@76Yse3L^gV0 z2==qfp&Q~L(+%RHP0n}+xH#k(hPRx(!AdBM$JCfJ5*C=K3ts>P?@@SZ_+{U2qFZb>4kZ{Go37{# zSQc+-dq*a-Vy4?taS&{Ht|MLRiS)Sn14JOONyXqPNnpq&2y~)6wEG0oNy>qvod$FF z`9o&?&6uZjhZ4_*5qWVrEfu(>_n2Xi2{@Gz9MZ8!YmjYvIMasE9yVQL10NBrTCczq zcTY1q^PF2l!Eraguf{+PtHV3=2A?Cu&NN&a8V(y;q(^_mFc6)%Yfn&X&~Pq zU1?qCj^LF(EQB1F`8NxNjyV%fde}dEa(Hx=r7$~ts2dzDwyi6ByBAIx$NllB4%K=O z$AHz1<2bTUb>(MCVPpK(E9wlLElo(aSd(Os)^Raum`d(g9Vd_+Bf&V;l=@mM=cC>) z)9b0enb)u_7V!!E_bl>u5nf&Rl|2r=2F3rHMdb7y9E}}F82^$Rf+P8%dKnOeKh1vs zhH^P*4Ydr^$)$h@4KVzxrHyy#cKmWEa9P5DJ|- zG;!Qi35Tp7XNj60=$!S6U#!(${6hyh7d4q=pF{`0t|N^|L^d8pD{O9@tF~W;#Je*P z&ah%W!KOIN;SyAEhAeTafJ4uEL`(RtnovM+cb(O#>xQnk?dzAjG^~4$dFn^<@-Na3 z395;wBnS{t*H;Jef2eE!2}u5Ns{AHj>WYZDgQJt8v%x?9{MXqJsGP|l%OiZqQ1aB! z%E=*Ig`(!tHh>}4_z5IMpg{49UvD*Pp9!pxt_gdAW%sIf3k6CTycOT1McPl=_#0?8 zVjz8Hj*Vy9c5-krd-{BQ{6Xy|P$6LJvMuX$* zA+@I_66_ET5l2&gk9n4$1M3LN8(yEViRx&mtd#LD}AqEs?RW=xKC(OCWH;~>(X6h!uDxXIPH06xh z*`F4cVlbDP`A)-fzf>MuScYsmq&1LUMGaQ3bRm6i7OsJ|%uhTDT zlvZA1M}nz*SalJWNT|`dBm1$xlaA>CCiQ zK`xD-RuEn>-`Z?M{1%@wewf#8?F|(@1e0+T4>nmlSRrNK5f)BJ2H*$q(H>zGD0>eL zQ!tl_Wk)k*e6v^m*{~A;@6+JGeWU-q9>?+L_#UNT%G?4&BnOgvm9@o7l?ov~XL+et zbGT)|G7)KAeqb=wHSPk+J1bdg7N3$vp(ekjI1D9V$G5Cj!=R2w=3*4!z*J-r-cyeb zd(i2KmX!|Lhey!snRw z?#$Gu%S^SQEKt&kep)up#j&9}e+3=JJBS(s>MH+|=R(`8xK{mmndWo_r`-w1#SeRD&YtAJ#GiVI*TkQZ}&aq<+bU2+coU3!jCI6E+Ad_xFW*ghnZ$q zAoF*i&3n1j#?B8x;kjSJD${1jdRB;)R*)Ao!9bd|C7{;iqDo|T&>KSh6*hCD!rwv= zyK#F@2+cv3=|S1Kef(E6Niv8kyLVLX&e=U;{0x{$tDfShqkjUME>f8d(5nzSkY6@! z^-0>DM)wa&%m#UF1F?zR`8Y3X#tA!*7Q$P3lZJ%*KNlrk_uaPkxw~ zxZ1qlE;Zo;nb@!SMazSjM>;34ROOoygo%SF);LL>rRonWwR>bmSd1XD^~sGSu$Gg# zFZ`|yKU0%!v07dz^v(tY%;So(e`o{ZYTX`hm;@b0%8|H>VW`*cr8R%3n|ehw2`(9B+V72`>SY}9^8oh$En80mZK9T4abVG*to;E z1_S6bgDOW?!Oy1LwYy=w3q~KKdbNtyH#d24PFjX)KYMY93{3-mPP-H>@M-_>N~DDu zENh~reh?JBAK=TFN-SfDfT^=+{w4ea2KNWXq2Y<;?(gf(FgVp8Zp-oEjKzB%2Iqj;48GmY3h=bcdYJ}~&4tS`Q1sb=^emaW$IC$|R+r-8V- zf0$gGE(CS_n4s>oicVk)MfvVg#I>iDvf~Ov8bk}sSxluG!6#^Z_zhB&U^`eIi1@j( z^CK$z^stBHtaDDHxn+R;3u+>Lil^}fj?7eaGB z&5nl^STqcaBxI@v>%zG|j))G(rVa4aY=B@^2{TFkW~YP!8!9TG#(-nOf^^X-%m9{Z zCC?iC`G-^RcBSCuk=Z`(FaUUe?hf3{0C>>$?Vs z`2Uud9M+T&KB6o4o9kvdi^Q=Bw!asPdxbe#W-Oaa#_NP(qpyF@bVxv5D5))srkU#m zj_KA+#7sqDn*Ipf!F5Byco4HOSd!Ui$l94|IbW%Ny(s1>f4|Mv^#NfB31N~kya9!k zWCGL-$0ZQztBate^fd>R!hXY_N9ZjYp3V~4_V z#eB)Kjr8yW=+oG)BuNdZG?jaZlw+l_ma8aET(s+-x+=F-t#Qoiuu1i`^x8Sj>b^U} zs^z<()YMFP7CmjUC@M=&lA5W7t&cxTlzJAts*%PBDAPuqcV5o7HEnqjif_7xGt)F% zGx2b4w{@!tE)$p=l3&?Bf#`+!-RLOleeRk3 z7#pF|w@6_sBmn1nECqdunmG^}pr5(ZJQVvAt$6p3H(16~;vO>?sTE`Y+mq5YP&PBo zvq!7#W$Gewy`;%6o^!Dtjz~x)T}Bdk*BS#=EY=ODD&B=V6TD2z^hj1m5^d6s)D*wk zu$z~D7QuZ2b?5`p)E8e2_L38v3WE{V`bVk;6fl#o2`) z99JsWhh?$oVRn@$S#)uK&8DL8>An0&S<%V8hnGD7Z^;Y(%6;^9!7kDQ5bjR_V+~wp zfx4m3z6CWmmZ<8gDGUyg3>t8wgJ5NkkiEm^(sedCicP^&3D%}6LtIUq>mXCAt{9eF zNXL$kGcoUTf_Lhm`t;hD-SE)m=iBnxRU(NyL}f6~1uH)`K!hmYZjLI%H}AmEF5RZt z06$wn63GHnApHXZZJ}s^s)j9(BM6e*7IBK6Bq(!)d~zR#rbxK9NVIlgquoMq z=eGZ9NR!SEqP6=9UQg#@!rtbbSBUM#ynF);zKX+|!Zm}*{H z+j=d?aZ2!?@EL7C~%B?6ouCKLnO$uWn;Y6Xz zX8dSwj732u(o*U3F$F=7xwxm>E-B+SVZH;O-4XPuPkLSt_?S0)lb7EEg)Mglk0#eS z9@jl(OnH4juMxY+*r03VDfPx_IM!Lmc(5hOI;`?d37f>jPP$?9jQQIQU@i4vuG6MagEoJrQ=RD7xt@8E;c zeGV*+Pt+t$@pt!|McETOE$9k=_C!70uhwRS9X#b%ZK z%q(TIUXSS^F0`4Cx?Rk07C6wI4!UVPeI~-fxY6`YH$kABdOuiRtl73MqG|~AzZ@iL&^s?24iS;RK_pdlWkhcF z@Wv-Om(Aealfg)D^adlXh9Nvf~Uf@y;g3Y)i(YP zEXDnb1V}1pJT5ZWyw=1i+0fni9yINurD=EqH^ciOwLUGi)C%Da)tyt=zq2P7pV5-G zR7!oq28-Fgn5pW|nlu^b!S1Z#r7!Wtr{5J5PQ>pd+2P7RSD?>(U7-|Y z7ZQ5lhYIl_IF<9?T9^IPK<(Hp;l5bl5tF9>X-zG14_7PfsA>6<$~A338iYRT{a@r_ zuXBaT=`T5x3=s&3=RYx6NgG>No4?5KFBVjE(swfcivcIpPQFx5l+O;fiGsOrl5teR z_Cm+;PW}O0Dwe_(4Z@XZ)O0W-v2X><&L*<~*q3dg;bQW3g7)a#3KiQP>+qj|qo*Hk z?57>f2?f@`=Fj^nkDKeRkN2d$Z@2eNKpHo}ksj-$`QKb6n?*$^*%Fb3_Kbf1(*W9K>{L$mud2WHJ=j0^=g30Xhg8$#g^?36`p1fm;;1@0Lrx+8t`?vN0ZorM zSW?rhjCE8$C|@p^sXdx z|NOHHg+fL;HIlqyLp~SSdIF`TnSHehNCU9t89yr@)FY<~hu+X`tjg(aSVae$wDG*C zq$nY(Y494R)hD!i1|IIyP*&PD_c2FPgeY)&mX1qujB1VHPG9`yFQpLFVQ0>EKS@Bp zAfP5`C(sWGLI?AC{XEjLKR4FVNw(4+9b?kba95ukgR1H?w<8F7)G+6&(zUhIE5Ef% z=fFkL3QKA~M@h{nzjRq!Y_t!%U66#L8!(2-GgFxkD1=JRRqk=n%G(yHKn%^&$dW>; zSjAcjETMz1%205se$iH_)ZCpfg_LwvnsZQAUCS#^FExp8O4CrJb6>JquNV@qPq~3A zZ<6dOU#6|8+fcgiA#~MDmcpIEaUO02L5#T$HV0$EMD94HT_eXLZ2Zi&(! z&5E>%&|FZ`)CN10tM%tLSPD*~r#--K(H-CZqIOb99_;m|D5wdgJ<1iOJz@h2Zkq?} z%8_KXb&hf=2Wza(Wgc;3v3TN*;HTU*q2?#z&tLn_U0Nt!y>Oo>+2T)He6%XuP;fgn z-G!#h$Y2`9>Jtf}hbVrm6D70|ERzLAU>3zoWhJmjWfgM^))T+2u$~5>HF9jQDkrXR z=IzX36)V75PrFjkQ%TO+iqKGCQ-DDXbaE;C#}!-CoWQx&v*vHfyI>$HNRbpvm<`O( zlx9NBWD6_e&J%Ous4yp~s6)Ghni!I6)0W;9(9$y1wWu`$gs<$9Mcf$L*piP zPR0Av*2%ul`W;?-1_-5Zy0~}?`e@Y5A&0H!^ApyVTT}BiOm4GeFo$_oPlDEyeGBbh z1h3q&Dx~GmUS|3@4V36&$2uO8!Yp&^pD7J5&TN{?xphf*-js1fP?B|`>p_K>lh{ij zP(?H%e}AIP?_i^f&Li=FDSQ`2_NWxL+BB=nQr=$ zHojMlXNGauvvwPU>ZLq!`bX-5F4jBJ&So{kE5+ms9UEYD{66!|k~3vsP+mE}x!>%P za98bAU0!h0&ka4EoiDvBM#CP#dRNdXJcb*(%=<(g+M@<)DZ!@v1V>;54En?igcHR2 zhubQMq}VSOK)onqHfczM7YA@s=9*ow;k;8)&?J3@0JiGcP! zP#00KZ1t)GyZeRJ=f0^gc+58lc4Qh*S7RqPIC6GugG1gXe$LIQMRCo8cHf^qXgAa2 z`}t>u2Cq1CbSEpLr~E=c7~=Qkc9-vLE%(v9N*&HF`(d~(0`iukl5aQ9u4rUvc8%m) zr2GwZN4!s;{SB87lJB;veebPmqE}tSpT>+`t?<457Q9iV$th%i__Z1kOMAswFldD6 ztbOvO337S5o#ZZgN2G99_AVqPv!?Gmt3pzgD+Hp3QPQ`9qJ(g=kjvD+fUSS3upJn! zqoG7acIKEFRX~S}3|{EWT$kdz#zrDlJU(rPkxjws_iyLKU8+v|*oS_W*-guAb&Pj1 z35Z`3z<&Jb@2Mwz=KXucNYdY#SNO$tcVFr9KdKm|%^e-TXzs6M`PBper%ajkrIyUe zp$vVxVs9*>Vp4_1NC~Zg)WOCPmOxI1V34QlG4!aSFOH{QqSVq1^1)- z0P!Z?tT&E-ll(pwf0?=F=yOzik=@nh1Clxr9}Vij89z)ePDSCYAqw?lVI?v?+&*zH z)p$CScFI8rrwId~`}9YWPFu0cW1Sf@vRELs&cbntRU6QfPK-SO*mqu|u~}8AJ!Q$z znzu}50O=YbjwKCuSVBs6&CZR#0FTu)3{}qJJYX(>QPr4$RqWiwX3NT~;>cLn*_&1H zaKpIW)JVJ>b{uo2oq>oQt3y=zJjb%fU@wLqM{SyaC6x2snMx-}ivfU<1- znu1Lh;i$3Tf$Kh5Uk))G!D1UhE8pvx&nO~w^fG)BC&L!_hQk%^p`Kp@F{cz>80W&T ziOK=Sq3fdRu*V0=S53rcIfWFazI}Twj63CG(jOB;$*b`*#B9uEnBM`hDk*EwSRdwP8?5T?xGUKs=5N83XsR*)a4|ijz|c{4tIU+4j^A5C<#5 z*$c_d=5ml~%pGxw#?*q9N7aRwPux5EyqHVkdJO=5J>84!X6P>DS8PTTz>7C#FO?k#edkntG+fJk8ZMn?pmJSO@`x-QHq;7^h6GEXLXo1TCNhH z8ZDH{*NLAjo3WM`xeb=X{((uv3H(8&r8fJJg_uSs_%hOH%JDD?hu*2NvWGYD+j)&` zz#_1%O1wF^o5ryt?O0n;`lHbzp0wQ?rcbW(F1+h7_EZZ9{>rePvLAPVZ_R|n@;b$;UchU=0j<6k8G9QuQf@76oiE*4 zXOLQ&n3$NR#p4<5NJMVC*S);5x2)eRbaAM%VxWu9ohlT;pGEk7;002enCbQ>2r-us z3#bpXP9g|mE`65VrN`+3mC)M(eMj~~eOf)do<@l+fMiTR)XO}422*1SL{wyY(%oMpBgJagtiDf zz>O6(m;};>Hi=t8o{DVC@YigqS(Qh+ix3Rwa9aliH}a}IlOCW1@?%h_bRbq-W{KHF z%Vo?-j@{Xi@=~Lz5uZP27==UGE15|g^0gzD|3x)SCEXrx`*MP^FDLl%pOi~~Il;dc z^hrwp9sYeT7iZ)-ajKy@{a`kr0-5*_!XfBpXwEcFGJ;%kV$0Nx;apKrur zJN2J~CAv{Zjj%FolyurtW8RaFmpn&zKJWL>(0;;+q(%(Hx!GMW4AcfP0YJ*Vz!F4g z!ZhMyj$BdXL@MlF%KeInmPCt~9&A!;cRw)W!Hi@0DY(GD_f?jeV{=s=cJ6e}JktJw zQORnxxj3mBxfrH=x{`_^Z1ddDh}L#V7i}$njUFRVwOX?qOTKjfPMBO4y(WiU<)epb zvB9L=%jW#*SL|Nd_G?E*_h1^M-$PG6Pc_&QqF0O-FIOpa4)PAEPsyvB)GKasmBoEt z?_Q2~QCYGH+hW31x-B=@5_AN870vY#KB~3a*&{I=f);3Kv7q4Q7s)0)gVYx2#Iz9g(F2;=+Iy4 z6KI^8GJ6D@%tpS^8boU}zpi=+(5GfIR)35PzrbuXeL1Y1N%JK7PG|^2k3qIqHfX;G zQ}~JZ-UWx|60P5?d1e;AHx!_;#PG%d=^X(AR%i`l0jSpYOpXoKFW~7ip7|xvN;2^? zsYC9fanpO7rO=V7+KXqVc;Q5z%Bj})xHVrgoR04sA2 zl~DAwv=!(()DvH*=lyhIlU^hBkA0$e*7&fJpB0|oB7)rqGK#5##2T`@_I^|O2x4GO z;xh6ROcV<9>?e0)MI(y++$-ksV;G;Xe`lh76T#Htuia+(UrIXrf9?

L(tZ$0BqX1>24?V$S+&kLZ`AodQ4_)P#Q3*4xg8}lMV-FLwC*cN$< zt65Rf%7z41u^i=P*qO8>JqXPrinQFapR7qHAtp~&RZ85$>ob|Js;GS^y;S{XnGiBc zGa4IGvDl?x%gY`vNhv8wgZnP#UYI-w*^4YCZnxkF85@ldepk$&$#3EAhrJY0U)lR{F6sM3SONV^+$;Zx8BD&Eku3K zKNLZyBni3)pGzU0;n(X@1fX8wYGKYMpLmCu{N5-}epPDxClPFK#A@02WM3!myN%bkF z|GJ4GZ}3sL{3{qXemy+#Uk{4>Kf8v11;f8I&c76+B&AQ8udd<8gU7+BeWC`akUU~U zgXoxie>MS@rBoyY8O8Tc&8id!w+_ooxcr!1?#rc$-|SBBtH6S?)1e#P#S?jFZ8u-Bs&k`yLqW|{j+%c#A4AQ>+tj$Y z^CZajspu$F%73E68Lw5q7IVREED9r1Ijsg#@DzH>wKseye>hjsk^{n0g?3+gs@7`i zHx+-!sjLx^fS;fY!ERBU+Q zVJ!e0hJH%P)z!y%1^ZyG0>PN@5W~SV%f>}c?$H8r;Sy-ui>aruVTY=bHe}$e zi&Q4&XK!qT7-XjCrDaufT@>ieQ&4G(SShUob0Q>Gznep9fR783jGuUynAqc6$pYX; z7*O@@JW>O6lKIk0G00xsm|=*UVTQBB`u1f=6wGAj%nHK_;Aqmfa!eAykDmi-@u%6~ z;*c!pS1@V8r@IX9j&rW&d*}wpNs96O2Ute>%yt{yv>k!6zfT6pru{F1M3P z2WN1JDYqoTB#(`kE{H676QOoX`cnqHl1Yaru)>8Ky~VU{)r#{&s86Vz5X)v15ULHA zAZDb{99+s~qI6;-dQ5DBjHJP@GYTwn;Dv&9kE<0R!d z8tf1oq$kO`_sV(NHOSbMwr=To4r^X$`sBW4$gWUov|WY?xccQJN}1DOL|GEaD_!@& z15p?Pj+>7d`@LvNIu9*^hPN)pwcv|akvYYq)ks%`G>!+!pW{-iXPZsRp8 z35LR;DhseQKWYSD`%gO&k$Dj6_6q#vjWA}rZcWtQr=Xn*)kJ9kacA=esi*I<)1>w^ zO_+E>QvjP)qiSZg9M|GNeLtO2D7xT6vsj`88sd!94j^AqxFLi}@w9!Y*?nwWARE0P znuI_7A-saQ+%?MFA$gttMV-NAR^#tjl_e{R$N8t2NbOlX373>e7Ox=l=;y#;M7asp zRCz*CLnrm$esvSb5{T<$6CjY zmZ(i{Rs_<#pWW>(HPaaYj`%YqBra=Ey3R21O7vUbzOkJJO?V`4-D*u4$Me0Bx$K(lYo`JO}gnC zx`V}a7m-hLU9Xvb@K2ymioF)vj12<*^oAqRuG_4u%(ah?+go%$kOpfb`T96P+L$4> zQ#S+sA%VbH&mD1k5Ak7^^dZoC>`1L%i>ZXmooA!%GI)b+$D&ziKrb)a=-ds9xk#~& z7)3iem6I|r5+ZrTRe_W861x8JpD`DDIYZNm{$baw+$)X^Jtjnl0xlBgdnNY}x%5za zkQ8E6T<^$sKBPtL4(1zi_Rd(tVth*3Xs!ulflX+70?gb&jRTnI8l+*Aj9{|d%qLZ+ z>~V9Z;)`8-lds*Zgs~z1?Fg?Po7|FDl(Ce<*c^2=lFQ~ahwh6rqSjtM5+$GT>3WZW zj;u~w9xwAhOc<kF}~`CJ68 z?(S5vNJa;kriPlim33{N5`C{9?NWhzsna_~^|K2k4xz1`xcui*LXL-1#Y}Hi9`Oo!zQ>x-kgAX4LrPz63uZ+?uG*84@PKq-KgQlMNRwz=6Yes) zY}>YN+qP}nwr$(CZQFjUOI=-6J$2^XGvC~EZ+vrqWaOXB$k?%Suf5k=4>AveC1aJ! ziaW4IS%F$_Babi)kA8Y&u4F7E%99OPtm=vzw$$ zEz#9rvn`Iot_z-r3MtV>k)YvErZ<^Oa${`2>MYYODSr6?QZu+be-~MBjwPGdMvGd!b!elsdi4% z`37W*8+OGulab8YM?`KjJ8e+jM(tqLKSS@=jimq3)Ea2EB%88L8CaM+aG7;27b?5` z4zuUWBr)f)k2o&xg{iZ$IQkJ+SK>lpq4GEacu~eOW4yNFLU!Kgc{w4&D$4ecm0f}~ zTTzquRW@`f0}|IILl`!1P+;69g^upiPA6F{)U8)muWHzexRenBU$E^9X-uIY2%&1w z_=#5*(nmxJ9zF%styBwivi)?#KMG96-H@hD-H_&EZiRNsfk7mjBq{L%!E;Sqn!mVX*}kXhwH6eh;b42eD!*~upVG@ z#smUqz$ICm!Y8wY53gJeS|Iuard0=;k5i5Z_hSIs6tr)R4n*r*rE`>38Pw&lkv{_r!jNN=;#?WbMj|l>cU(9trCq; z%nN~r^y7!kH^GPOf3R}?dDhO=v^3BeP5hF|%4GNQYBSwz;x({21i4OQY->1G=KFyu z&6d`f2tT9Yl_Z8YACZaJ#v#-(gcyeqXMhYGXb=t>)M@fFa8tHp2x;ODX=Ap@a5I=U z0G80^$N0G4=U(>W%mrrThl0DjyQ-_I>+1Tdd_AuB3qpYAqY54upwa3}owa|x5iQ^1 zEf|iTZxKNGRpI>34EwkIQ2zHDEZ=(J@lRaOH>F|2Z%V_t56Km$PUYu^xA5#5Uj4I4RGqHD56xT%H{+P8Ag>e_3pN$4m8n>i%OyJFPNWaEnJ4McUZPa1QmOh?t8~n& z&RulPCors8wUaqMHECG=IhB(-tU2XvHP6#NrLVyKG%Ee*mQ5Ps%wW?mcnriTVRc4J`2YVM>$ixSF2Xi+Wn(RUZnV?mJ?GRdw%lhZ+t&3s7g!~g{%m&i<6 z5{ib-<==DYG93I(yhyv4jp*y3#*WNuDUf6`vTM%c&hiayf(%=x@4$kJ!W4MtYcE#1 zHM?3xw63;L%x3drtd?jot!8u3qeqctceX3m;tWetK+>~q7Be$h>n6riK(5@ujLgRS zvOym)k+VAtyV^mF)$29Y`nw&ijdg~jYpkx%*^ z8dz`C*g=I?;clyi5|!27e2AuSa$&%UyR(J3W!A=ZgHF9OuKA34I-1U~pyD!KuRkjA zbkN!?MfQOeN>DUPBxoy5IX}@vw`EEB->q!)8fRl_mqUVuRu|C@KD-;yl=yKc=ZT0% zB$fMwcC|HE*0f8+PVlWHi>M`zfsA(NQFET?LrM^pPcw`cK+Mo0%8*x8@65=CS_^$cG{GZQ#xv($7J z??R$P)nPLodI;P!IC3eEYEHh7TV@opr#*)6A-;EU2XuogHvC;;k1aI8asq7ovoP!* z?x%UoPrZjj<&&aWpsbr>J$Er-7!E(BmOyEv!-mbGQGeJm-U2J>74>o5x`1l;)+P&~ z>}f^=Rx(ZQ2bm+YE0u=ZYrAV@apyt=v1wb?R@`i_g64YyAwcOUl=C!i>=Lzb$`tjv zOO-P#A+)t-JbbotGMT}arNhJmmGl-lyUpMn=2UacVZxmiG!s!6H39@~&uVokS zG=5qWhfW-WOI9g4!R$n7!|ViL!|v3G?GN6HR0Pt_L5*>D#FEj5wM1DScz4Jv@Sxnl zB@MPPmdI{(2D?;*wd>3#tjAirmUnQoZrVv`xM3hARuJksF(Q)wd4P$88fGYOT1p6U z`AHSN!`St}}UMBT9o7i|G`r$ zrB=s$qV3d6$W9@?L!pl0lf%)xs%1ko^=QY$ty-57=55PvP(^6E7cc zGJ*>m2=;fOj?F~yBf@K@9qwX0hA803Xw+b0m}+#a(>RyR8}*Y<4b+kpp|OS+!whP( zH`v{%s>jsQI9rd$*vm)EkwOm#W_-rLTHcZRek)>AtF+~<(did)*oR1|&~1|e36d-d zgtm5cv1O0oqgWC%Et@P4Vhm}Ndl(Y#C^MD03g#PH-TFy+7!Osv1z^UWS9@%JhswEq~6kSr2DITo59+; ze=ZC}i2Q?CJ~Iyu?vn|=9iKV>4j8KbxhE4&!@SQ^dVa-gK@YfS9xT(0kpW*EDjYUkoj! zE49{7H&E}k%5(>sM4uGY)Q*&3>{aitqdNnRJkbOmD5Mp5rv-hxzOn80QsG=HJ_atI-EaP69cacR)Uvh{G5dTpYG7d zbtmRMq@Sexey)||UpnZ?;g_KMZq4IDCy5}@u!5&B^-=6yyY{}e4Hh3ee!ZWtL*s?G zxG(A!<9o!CL+q?u_utltPMk+hn?N2@?}xU0KlYg?Jco{Yf@|mSGC<(Zj^yHCvhmyx z?OxOYoxbptDK()tsJ42VzXdINAMWL$0Gcw?G(g8TMB)Khw_|v9`_ql#pRd2i*?CZl z7k1b!jQB=9-V@h%;Cnl7EKi;Y^&NhU0mWEcj8B|3L30Ku#-9389Q+(Yet0r$F=+3p z6AKOMAIi|OHyzlHZtOm73}|ntKtFaXF2Fy|M!gOh^L4^62kGUoWS1i{9gsds_GWBc zLw|TaLP64z3z9?=R2|T6Xh2W4_F*$cq>MtXMOy&=IPIJ`;!Tw?PqvI2b*U1)25^<2 zU_ZPoxg_V0tngA0J+mm?3;OYw{i2Zb4x}NedZug!>EoN3DC{1i)Z{Z4m*(y{ov2%- zk(w>+scOO}MN!exSc`TN)!B=NUX`zThWO~M*ohqq;J2hx9h9}|s#?@eR!=F{QTrq~ zTcY|>azkCe$|Q0XFUdpFT=lTcyW##i;-e{}ORB4D?t@SfqGo_cS z->?^rh$<&n9DL!CF+h?LMZRi)qju!meugvxX*&jfD!^1XB3?E?HnwHP8$;uX{Rvp# zh|)hM>XDv$ZGg=$1{+_bA~u-vXqlw6NH=nkpyWE0u}LQjF-3NhATL@9rRxMnpO%f7 z)EhZf{PF|mKIMFxnC?*78(}{Y)}iztV12}_OXffJ;ta!fcFIVjdchyHxH=t%ci`Xd zX2AUB?%?poD6Zv*&BA!6c5S#|xn~DK01#XvjT!w!;&`lDXSJT4_j$}!qSPrb37vc{ z9^NfC%QvPu@vlxaZ;mIbn-VHA6miwi8qJ~V;pTZkKqqOii<1Cs}0i?uUIss;hM4dKq^1O35y?Yp=l4i zf{M!@QHH~rJ&X~8uATV><23zZUbs-J^3}$IvV_ANLS08>k`Td7aU_S1sLsfi*C-m1 z-e#S%UGs4E!;CeBT@9}aaI)qR-6NU@kvS#0r`g&UWg?fC7|b^_HyCE!8}nyh^~o@< zpm7PDFs9yxp+byMS(JWm$NeL?DNrMCNE!I^ko-*csB+dsf4GAq{=6sfyf4wb>?v1v zmb`F*bN1KUx-`ra1+TJ37bXNP%`-Fd`vVQFTwWpX@;s(%nDQa#oWhgk#mYlY*!d>( zE&!|ySF!mIyfING+#%RDY3IBH_fW$}6~1%!G`suHub1kP@&DoAd5~7J55;5_noPI6eLf{t;@9Kf<{aO0`1WNKd?<)C-|?C?)3s z>wEq@8=I$Wc~Mt$o;g++5qR+(6wt9GI~pyrDJ%c?gPZe)owvy^J2S=+M^ z&WhIE`g;;J^xQLVeCtf7b%Dg#Z2gq9hp_%g)-%_`y*zb; zn9`f`mUPN-Ts&fFo(aNTsXPA|J!TJ{0hZp0^;MYHLOcD=r_~~^ymS8KLCSeU3;^QzJNqS z5{5rEAv#l(X?bvwxpU;2%pQftF`YFgrD1jt2^~Mt^~G>T*}A$yZc@(k9orlCGv&|1 zWWvVgiJsCAtamuAYT~nzs?TQFt<1LSEx!@e0~@yd6$b5!Zm(FpBl;(Cn>2vF?k zOm#TTjFwd2D-CyA!mqR^?#Uwm{NBemP>(pHmM}9;;8`c&+_o3#E5m)JzfwN?(f-a4 zyd%xZc^oQx3XT?vcCqCX&Qrk~nu;fxs@JUoyVoi5fqpi&bUhQ2y!Ok2pzsFR(M(|U zw3E+kH_zmTRQ9dUMZWRE%Zakiwc+lgv7Z%|YO9YxAy`y28`Aw;WU6HXBgU7fl@dnt z-fFBV)}H-gqP!1;V@Je$WcbYre|dRdp{xt!7sL3Eoa%IA`5CAA%;Wq8PktwPdULo! z8!sB}Qt8#jH9Sh}QiUtEPZ6H0b*7qEKGJ%ITZ|vH)5Q^2m<7o3#Z>AKc%z7_u`rXA zqrCy{-{8;9>dfllLu$^M5L z-hXs))h*qz%~ActwkIA(qOVBZl2v4lwbM>9l70Y`+T*elINFqt#>OaVWoja8RMsep z6Or3f=oBnA3vDbn*+HNZP?8LsH2MY)x%c13@(XfuGR}R?Nu<|07{$+Lc3$Uv^I!MQ z>6qWgd-=aG2Y^24g4{Bw9ueOR)(9h`scImD=86dD+MnSN4$6 z^U*o_mE-6Rk~Dp!ANp#5RE9n*LG(Vg`1)g6!(XtDzsov$Dvz|Gv1WU68J$CkshQhS zCrc|cdkW~UK}5NeaWj^F4MSgFM+@fJd{|LLM)}_O<{rj z+?*Lm?owq?IzC%U%9EBga~h-cJbIu=#C}XuWN>OLrc%M@Gu~kFEYUi4EC6l#PR2JS zQUkGKrrS#6H7}2l0F@S11DP`@pih0WRkRJl#F;u{c&ZC{^$Z+_*lB)r)-bPgRFE;* zl)@hK4`tEP=P=il02x7-C7p%l=B`vkYjw?YhdJU9!P!jcmY$OtC^12w?vy3<<=tlY zUwHJ_0lgWN9vf>1%WACBD{UT)1qHQSE2%z|JHvP{#INr13jM}oYv_5#xsnv9`)UAO zuwgyV4YZ;O)eSc3(mka6=aRohi!HH@I#xq7kng?Acdg7S4vDJb6cI5fw?2z%3yR+| zU5v@Hm}vy;${cBp&@D=HQ9j7NcFaOYL zj-wV=eYF{|XTkFNM2uz&T8uH~;)^Zo!=KP)EVyH6s9l1~4m}N%XzPpduPg|h-&lL` zAXspR0YMOKd2yO)eMFFJ4?sQ&!`dF&!|niH*!^*Ml##o0M(0*uK9&yzekFi$+mP9s z>W9d%Jb)PtVi&-Ha!o~Iyh@KRuKpQ@)I~L*d`{O8!kRObjO7=n+Gp36fe!66neh+7 zW*l^0tTKjLLzr`x4`_8&on?mjW-PzheTNox8Hg7Nt@*SbE-%kP2hWYmHu#Fn@Q^J(SsPUz*|EgOoZ6byg3ew88UGdZ>9B2Tq=jF72ZaR=4u%1A6Vm{O#?@dD!(#tmR;eP(Fu z{$0O%=Vmua7=Gjr8nY%>ul?w=FJ76O2js&17W_iq2*tb!i{pt#`qZB#im9Rl>?t?0c zicIC}et_4d+CpVPx)i4~$u6N-QX3H77ez z?ZdvXifFk|*F8~L(W$OWM~r`pSk5}#F?j_5u$Obu9lDWIknO^AGu+Blk7!9Sb;NjS zncZA?qtASdNtzQ>z7N871IsPAk^CC?iIL}+{K|F@BuG2>qQ;_RUYV#>hHO(HUPpk@ z(bn~4|F_jiZi}Sad;_7`#4}EmD<1EiIxa48QjUuR?rC}^HRocq`OQPM@aHVKP9E#q zy%6bmHygCpIddPjE}q_DPC`VH_2m;Eey&ZH)E6xGeStOK7H)#+9y!%-Hm|QF6w#A( zIC0Yw%9j$s-#odxG~C*^MZ?M<+&WJ+@?B_QPUyTg9DJGtQN#NIC&-XddRsf3n^AL6 zT@P|H;PvN;ZpL0iv$bRb7|J{0o!Hq+S>_NrH4@coZtBJu#g8#CbR7|#?6uxi8d+$g z87apN>EciJZ`%Zv2**_uiET9Vk{pny&My;+WfGDw4EVL#B!Wiw&M|A8f1A@ z(yFQS6jfbH{b8Z-S7D2?Ixl`j0{+ZnpT=;KzVMLW{B$`N?Gw^Fl0H6lT61%T2AU**!sX0u?|I(yoy&Xveg7XBL&+>n6jd1##6d>TxE*Vj=8lWiG$4=u{1UbAa5QD>5_ z;Te^42v7K6Mmu4IWT6Rnm>oxrl~b<~^e3vbj-GCdHLIB_>59}Ya+~OF68NiH=?}2o zP(X7EN=quQn&)fK>M&kqF|<_*H`}c zk=+x)GU>{Af#vx&s?`UKUsz})g^Pc&?Ka@t5$n$bqf6{r1>#mWx6Ep>9|A}VmWRnowVo`OyCr^fHsf# zQjQ3Ttp7y#iQY8l`zEUW)(@gGQdt(~rkxlkefskT(t%@i8=|p1Y9Dc5bc+z#n$s13 zGJk|V0+&Ekh(F};PJzQKKo+FG@KV8a<$gmNSD;7rd_nRdc%?9)p!|B-@P~kxQG}~B zi|{0}@}zKC(rlFUYp*dO1RuvPC^DQOkX4<+EwvBAC{IZQdYxoq1Za!MW7%p7gGr=j zzWnAq%)^O2$eItftC#TTSArUyL$U54-O7e|)4_7%Q^2tZ^0-d&3J1}qCzR4dWX!)4 zzIEKjgnYgMus^>6uw4Jm8ga6>GBtMjpNRJ6CP~W=37~||gMo_p@GA@#-3)+cVYnU> zE5=Y4kzl+EbEh%dhQokB{gqNDqx%5*qBusWV%!iprn$S!;oN_6E3?0+umADVs4ako z?P+t?m?};gev9JXQ#Q&KBpzkHPde_CGu-y z<{}RRAx=xlv#mVi+Ibrgx~ujW$h{?zPfhz)Kp7kmYS&_|97b&H&1;J-mzrBWAvY} zh8-I8hl_RK2+nnf&}!W0P+>5?#?7>npshe<1~&l_xqKd0_>dl_^RMRq@-Myz&|TKZBj1=Q()) zF{dBjv5)h=&Z)Aevx}+i|7=R9rG^Di!sa)sZCl&ctX4&LScQ-kMncgO(9o6W6)yd< z@Rk!vkja*X_N3H=BavGoR0@u0<}m-7|2v!0+2h~S2Q&a=lTH91OJsvms2MT~ zY=c@LO5i`mLpBd(vh|)I&^A3TQLtr>w=zoyzTd=^f@TPu&+*2MtqE$Avf>l>}V|3-8Fp2hzo3y<)hr_|NO(&oSD z!vEjTWBxbKTiShVl-U{n*B3#)3a8$`{~Pk}J@elZ=>Pqp|MQ}jrGv7KrNcjW%TN_< zZz8kG{#}XoeWf7qY?D)L)8?Q-b@Na&>i=)(@uNo zr;cH98T3$Iau8Hn*@vXi{A@YehxDE2zX~o+RY`)6-X{8~hMpc#C`|8y> zU8Mnv5A0dNCf{Ims*|l-^ z(MRp{qoGohB34|ggDI*p!Aw|MFyJ|v+<+E3brfrI)|+l3W~CQLPbnF@G0)P~Ly!1TJLp}xh8uW`Q+RB-v`MRYZ9Gam3cM%{ zb4Cb*f)0deR~wtNb*8w-LlIF>kc7DAv>T0D(a3@l`k4TFnrO+g9XH7;nYOHxjc4lq zMmaW6qpgAgy)MckYMhl?>sq;-1E)-1llUneeA!ya9KM$)DaNGu57Z5aE>=VST$#vb zFo=uRHr$0M{-ha>h(D_boS4zId;3B|Tpqo|?B?Z@I?G(?&Iei+-{9L_A9=h=Qfn-U z1wIUnQe9!z%_j$F_{rf&`ZFSott09gY~qrf@g3O=Y>vzAnXCyL!@(BqWa)Zqt!#_k zfZHuwS52|&&)aK;CHq9V-t9qt0au{$#6c*R#e5n3rje0hic7c7m{kW$p(_`wB=Gw7 z4k`1Hi;Mc@yA7dp@r~?@rfw)TkjAW++|pkfOG}0N|2guek}j8Zen(!+@7?qt_7ndX zB=BG6WJ31#F3#Vk3=aQr8T)3`{=p9nBHlKzE0I@v`{vJ}h8pd6vby&VgFhzH|q;=aonunAXL6G2y(X^CtAhWr*jI zGjpY@raZDQkg*aMq}Ni6cRF z{oWv}5`nhSAv>usX}m^GHt`f(t8@zHc?K|y5Zi=4G*UG1Sza{$Dpj%X8 zzEXaKT5N6F5j4J|w#qlZP!zS7BT)9b+!ZSJdToqJts1c!)fwih4d31vfb{}W)EgcA zH2pZ^8_k$9+WD2n`6q5XbOy8>3pcYH9 z07eUB+p}YD@AH!}p!iKv><2QF-Y^&xx^PAc1F13A{nUeCDg&{hnix#FiO!fe(^&%Qcux!h znu*S!s$&nnkeotYsDthh1dq(iQrE|#f_=xVgfiiL&-5eAcC-> z5L0l|DVEM$#ulf{bj+Y~7iD)j<~O8CYM8GW)dQGq)!mck)FqoL^X zwNdZb3->hFrbHFm?hLvut-*uK?zXn3q1z|UX{RZ;-WiLoOjnle!xs+W0-8D)kjU#R z+S|A^HkRg$Ij%N4v~k`jyHffKaC~=wg=9)V5h=|kLQ@;^W!o2^K+xG&2n`XCd>OY5Ydi= zgHH=lgy++erK8&+YeTl7VNyVm9-GfONlSlVb3)V9NW5tT!cJ8d7X)!b-$fb!s76{t z@d=Vg-5K_sqHA@Zx-L_}wVnc@L@GL9_K~Zl(h5@AR#FAiKad8~KeWCo@mgXIQ#~u{ zgYFwNz}2b6Vu@CP0XoqJ+dm8px(5W5-Jpis97F`+KM)TuP*X8H@zwiVKDKGVp59pI zifNHZr|B+PG|7|Y<*tqap0CvG7tbR1R>jn70t1X`XJixiMVcHf%Ez*=xm1(CrTSDt z0cle!+{8*Ja&EOZ4@$qhBuKQ$U95Q%rc7tg$VRhk?3=pE&n+T3upZg^ZJc9~c2es% zh7>+|mrmA-p&v}|OtxqmHIBgUxL~^0+cpfkSK2mhh+4b=^F1Xgd2)}U*Yp+H?ls#z zrLxWg_hm}AfK2XYWr!rzW4g;+^^&bW%LmbtRai9f3PjU${r@n`JThy-cphbcwn)rq9{A$Ht`lmYKxOacy z6v2R(?gHhD5@&kB-Eg?4!hAoD7~(h>(R!s1c1Hx#s9vGPePUR|of32bS`J5U5w{F) z>0<^ktO2UHg<0{oxkdOQ;}coZDQph8p6ruj*_?uqURCMTac;>T#v+l1Tc~%^k-Vd@ zkc5y35jVNc49vZpZx;gG$h{%yslDI%Lqga1&&;mN{Ush1c7p>7e-(zp}6E7f-XmJb4nhk zb8zS+{IVbL$QVF8pf8}~kQ|dHJAEATmmnrb_wLG}-yHe>W|A&Y|;muy-d^t^<&)g5SJfaTH@P1%euONny=mxo+C z4N&w#biWY41r8k~468tvuYVh&XN&d#%QtIf9;iVXfWY)#j=l`&B~lqDT@28+Y!0E+MkfC}}H*#(WKKdJJq=O$vNYCb(ZG@p{fJgu;h z21oHQ(14?LeT>n5)s;uD@5&ohU!@wX8w*lB6i@GEH0pM>YTG+RAIWZD;4#F1&F%Jp zXZUml2sH0!lYJT?&sA!qwez6cXzJEd(1ZC~kT5kZSp7(@=H2$Azb_*W&6aA|9iwCL zdX7Q=42;@dspHDwYE?miGX#L^3xD&%BI&fN9^;`v4OjQXPBaBmOF1;#C)8XA(WFlH zycro;DS2?(G&6wkr6rqC>rqDv3nfGw3hmN_9Al>TgvmGsL8_hXx09};l9Ow@)F5@y z#VH5WigLDwZE4nh^7&@g{1FV^UZ%_LJ-s<{HN*2R$OPg@R~Z`c-ET*2}XB@9xvAjrK&hS=f|R8Gr9 zr|0TGOsI7RD+4+2{ZiwdVD@2zmg~g@^D--YL;6UYGSM8i$NbQr4!c7T9rg!8;TM0E zT#@?&S=t>GQm)*ua|?TLT2ktj#`|R<_*FAkOu2Pz$wEc%-=Y9V*$&dg+wIei3b*O8 z2|m$!jJG!J!ZGbbIa!(Af~oSyZV+~M1qGvelMzPNE_%5?c2>;MeeG2^N?JDKjFYCy z7SbPWH-$cWF9~fX%9~v99L!G(wi!PFp>rB!9xj7=Cv|F+7CsGNwY0Q_J%FID%C^CBZQfJ9K(HK%k31j~e#&?hQ zNuD6gRkVckU)v+53-fc} z7ZCzYN-5RG4H7;>>Hg?LU9&5_aua?A0)0dpew1#MMlu)LHe(M;OHjHIUl7|%%)YPo z0cBk;AOY00%Fe6heoN*$(b<)Cd#^8Iu;-2v@>cE-OB$icUF9EEoaC&q8z9}jMTT2I z8`9;jT%z0;dy4!8U;GW{i`)3!c6&oWY`J3669C!tM<5nQFFrFRglU8f)5Op$GtR-3 zn!+SPCw|04sv?%YZ(a7#L?vsdr7ss@WKAw&A*}-1S|9~cL%uA+E~>N6QklFE>8W|% zyX-qAUGTY1hQ-+um`2|&ji0cY*(qN!zp{YpDO-r>jPk*yuVSay<)cUt`t@&FPF_&$ zcHwu1(SQ`I-l8~vYyUxm@D1UEdFJ$f5Sw^HPH7b!9 zzYT3gKMF((N(v0#4f_jPfVZ=ApN^jQJe-X$`A?X+vWjLn_%31KXE*}5_}d8 zw_B1+a#6T1?>M{ronLbHIlEsMf93muJ7AH5h%;i99<~JX^;EAgEB1uHralD*!aJ@F zV2ruuFe9i2Q1C?^^kmVy921eb=tLDD43@-AgL^rQ3IO9%+vi_&R2^dpr}x{bCVPej z7G0-0o64uyWNtr*loIvslyo0%)KSDDKjfThe0hcqs)(C-MH1>bNGBDRTW~scy_{w} zp^aq8Qb!h9Lwielq%C1b8=?Z=&U)ST&PHbS)8Xzjh2DF?d{iAv)Eh)wsUnf>UtXN( zL7=$%YrZ#|^c{MYmhn!zV#t*(jdmYdCpwqpZ{v&L8KIuKn`@IIZfp!uo}c;7J57N` zAxyZ-uA4=Gzl~Ovycz%MW9ZL7N+nRo&1cfNn9(1H5eM;V_4Z_qVann7F>5f>%{rf= zPBZFaV@_Sobl?Fy&KXyzFDV*FIdhS5`Uc~S^Gjo)aiTHgn#<0C=9o-a-}@}xDor;D zZyZ|fvf;+=3MZd>SR1F^F`RJEZo+|MdyJYQAEauKu%WDol~ayrGU3zzbHKsnHKZ*z zFiwUkL@DZ>!*x05ql&EBq@_Vqv83&?@~q5?lVmffQZ+V-=qL+!u4Xs2Z2zdCQ3U7B&QR9_Iggy} z(om{Y9eU;IPe`+p1ifLx-XWh?wI)xU9ik+m#g&pGdB5Bi<`PR*?92lE0+TkRuXI)z z5LP!N2+tTc%cB6B1F-!fj#}>S!vnpgVU~3!*U1ej^)vjUH4s-bd^%B=ItQqDCGbrEzNQi(dJ`J}-U=2{7-d zK8k^Rlq2N#0G?9&1?HSle2vlkj^KWSBYTwx`2?9TU_DX#J+f+qLiZCqY1TXHFxXZqYMuD@RU$TgcnCC{_(vwZ-*uX)~go#%PK z@}2Km_5aQ~(<3cXeJN6|F8X_1@L%@xTzs}$_*E|a^_URF_qcF;Pfhoe?FTFwvjm1o z8onf@OY@jC2tVcMaZS;|T!Ks(wOgPpRzRnFS-^RZ4E!9dsnj9sFt609a|jJbb1Dt@ z<=Gal2jDEupxUSwWu6zp<<&RnAA;d&4gKVG0iu6g(DsST(4)z6R)zDpfaQ}v{5ARt zyhwvMtF%b-YazR5XLz+oh=mn;y-Mf2a8>7?2v8qX;19y?b>Z5laGHvzH;Nu9S`B8} zI)qN$GbXIQ1VL3lnof^6TS~rvPVg4V?Dl2Bb*K2z4E{5vy<(@@K_cN@U>R!>aUIRnb zL*)=787*cs#zb31zBC49x$`=fkQbMAef)L2$dR{)6BAz!t5U_B#1zZG`^neKSS22oJ#5B=gl%U=WeqL9REF2g zZnfCb0?quf?Ztj$VXvDSWoK`0L=Zxem2q}!XWLoT-kYMOx)!7fcgT35uC~0pySEme z`{wGWTkGr7>+Kb^n;W?BZH6ZP(9tQX%-7zF>vc2}LuWDI(9kh1G#7B99r4x6;_-V+k&c{nPUrR zAXJGRiMe~aup{0qzmLNjS_BC4cB#sXjckx{%_c&^xy{M61xEb>KW_AG5VFXUOjAG4 z^>Qlm9A#1N{4snY=(AmWzatb!ngqiqPbBZ7>Uhb3)dTkSGcL#&SH>iMO-IJBPua`u zo)LWZ>=NZLr758j{%(|uQuZ)pXq_4c!!>s|aDM9#`~1bzK3J1^^D#<2bNCccH7~-X}Ggi!pIIF>uFx%aPARGQsnC8ZQc8lrQ5o~smqOg>Ti^GNme94*w z)JZy{_{#$jxGQ&`M z!OMvZMHR>8*^>eS%o*6hJwn!l8VOOjZQJvh)@tnHVW&*GYPuxqXw}%M!(f-SQf`=L z5;=5w2;%82VMH6Xi&-K3W)o&K^+vJCepWZ-rW%+Dc6X3(){z$@4zjYxQ|}8UIojeC zYZpQ1dU{fy=oTr<4VX?$q)LP}IUmpiez^O&N3E_qPpchGTi5ZM6-2ScWlQq%V&R2Euz zO|Q0Hx>lY1Q1cW5xHv5!0OGU~PVEqSuy#fD72d#O`N!C;o=m+YioGu-wH2k6!t<~K zSr`E=W9)!g==~x9VV~-8{4ZN9{~-A9zJpRe%NGg$+MDuI-dH|b@BD)~>pPCGUNNzY zMDg||0@XGQgw`YCt5C&A{_+J}mvV9Wg{6V%2n#YSRN{AP#PY?1FF1#|vO_%e+#`|2*~wGAJaeRX6=IzFNeWhz6gJc8+(03Ph4y6ELAm=AkN7TOgMUEw*N{= z_)EIDQx5q22oUR+_b*tazu9+pX|n1c*IB-}{DqIj z-?E|ks{o3AGRNb;+iKcHkZvYJvFsW&83RAPs1Oh@IWy%l#5x2oUP6ZCtv+b|q>jsf zZ_9XO;V!>n`UxH1LvH8)L4?8raIvasEhkpQoJ`%!5rBs!0Tu(s_D{`4opB;57)pkX z4$A^8CsD3U5*!|bHIEqsn~{q+Ddj$ME@Gq4JXtgVz&7l{Ok!@?EA{B3P~NAqb9)4? zkQo30A^EbHfQ@87G5&EQTd`frrwL)&Yw?%-W@uy^Gn23%j?Y!Iea2xw<-f;esq zf%w5WN@E1}zyXtYv}}`U^B>W`>XPmdLj%4{P298|SisrE;7HvXX;A}Ffi8B#3Lr;1 zHt6zVb`8{#+e$*k?w8|O{Uh|&AG}|DG1PFo1i?Y*cQm$ZwtGcVgMwtBUDa{~L1KT-{jET4w60>{KZ27vXrHJ;fW{6| z=|Y4!&UX020wU1>1iRgB@Q#m~1^Z^9CG1LqDhYBrnx%IEdIty z!46iOoKlKs)c}newDG)rWUikD%j`)p z_w9Ph&e40=(2eBy;T!}*1p1f1SAUDP9iWy^u^Ubdj21Kn{46;GR+hwLO=4D11@c~V zI8x&(D({K~Df2E)Nx_yQvYfh4;MbMJ@Z}=Dt3_>iim~QZ*hZIlEs0mEb z_54+&*?wMD`2#vsQRN3KvoT>hWofI_Vf(^C1ff-Ike@h@saEf7g}<9T`W;HAne-Nd z>RR+&SP35w)xKn8^U$7))PsM!jKwYZ*RzEcG-OlTrX3}9a{q%#Un5E5W{{hp>w~;` zGky+3(vJvQyGwBo`tCpmo0mo((?nM8vf9aXrrY1Ve}~TuVkB(zeds^jEfI}xGBCM2 zL1|#tycSaWCurP+0MiActG3LCas@_@tao@(R1ANlwB$4K53egNE_;!&(%@Qo$>h`^1S_!hN6 z)vZtG$8fN!|BXBJ=SI>e(LAU(y(i*PHvgQ2llulxS8>qsimv7yL}0q_E5WiAz7)(f zC(ahFvG8&HN9+6^jGyLHM~$)7auppeWh_^zKk&C_MQ~8;N??OlyH~azgz5fe^>~7F zl3HnPN3z-kN)I$4@`CLCMQx3sG~V8hPS^}XDXZrQA>}mQPw%7&!sd(Pp^P=tgp-s^ zjl}1-KRPNWXgV_K^HkP__SR`S-|OF0bR-N5>I%ODj&1JUeAQ3$9i;B~$S6}*^tK?= z**%aCiH7y?xdY?{LgVP}S0HOh%0%LI$wRx;$T|~Y8R)Vdwa}kGWv8?SJVm^>r6+%I z#lj1aR94{@MP;t-scEYQWc#xFA30^}?|BeX*W#9OL;Q9#WqaaM546j5j29((^_8Nu z4uq}ESLr~r*O7E7$D{!k9W>`!SLoyA53i9QwRB{!pHe8um|aDE`Cg0O*{jmor)^t)3`>V>SWN-2VJcFmj^1?~tT=JrP`fVh*t zXHarp=8HEcR#vFe+1a%XXuK+)oFs`GDD}#Z+TJ}Ri`FvKO@ek2ayn}yaOi%(8p%2$ zpEu)v0Jym@f}U|-;}CbR=9{#<^z28PzkkTNvyKvJDZe+^VS2bES3N@Jq!-*}{oQlz z@8bgC_KnDnT4}d#&Cpr!%Yb?E!brx0!eVOw~;lLwUoz#Np%d$o%9scc3&zPm`%G((Le|6o1 zM(VhOw)!f84zG^)tZ1?Egv)d8cdNi+T${=5kV+j;Wf%2{3g@FHp^Gf*qO0q!u$=m9 zCaY`4mRqJ;FTH5`a$affE5dJrk~k`HTP_7nGTY@B9o9vvnbytaID;^b=Tzp7Q#DmD zC(XEN)Ktn39z5|G!wsVNnHi) z%^q94!lL|hF`IijA^9NR0F$@h7k5R^ljOW(;Td9grRN0Mb)l_l7##{2nPQ@?;VjXv zaLZG}yuf$r$<79rVPpXg?6iiieX|r#&`p#Con2i%S8*8F}(E) zI5E6c3tG*<;m~6>!&H!GJ6zEuhH7mkAzovdhLy;)q z{H2*8I^Pb}xC4s^6Y}6bJvMu=8>g&I)7!N!5QG$xseeU#CC?ZM-TbjsHwHgDGrsD= z{%f;@Sod+Ch66Ko2WF~;Ty)v>&x^aovCbCbD7>qF*!?BXmOV3(s|nxsb*Lx_2lpB7 zokUnzrk;P=T-&kUHO}td+Zdj!3n&NR?K~cRU zAXU!DCp?51{J4w^`cV#ye}(`SQhGQkkMu}O3M*BWt4UsC^jCFUy;wTINYmhD$AT;4 z?Xd{HaJjP`raZ39qAm;%beDbrLpbRf(mkKbANan7XsL>_pE2oo^$TgdidjRP!5-`% zv0d!|iKN$c0(T|L0C~XD0aS8t{*&#LnhE;1Kb<9&=c2B+9JeLvJr*AyyRh%@jHej=AetOMSlz^=!kxX>>B{2B1uIrQyfd8KjJ+DBy!h)~*(!|&L4^Q_07SQ~E zcemVP`{9CwFvPFu7pyVGCLhH?LhEVb2{7U+Z_>o25#+3<|8%1T^5dh}*4(kfJGry} zm%r#hU+__Z;;*4fMrX=Bkc@7|v^*B;HAl0((IBPPii%X9+u3DDF6%bI&6?Eu$8&aWVqHIM7mK6?Uvq$1|(-T|)IV<>e?!(rY zqkmO1MRaLeTR=)io(0GVtQT@s6rN%C6;nS3@eu;P#ry4q;^O@1ZKCJyp_Jo)Ty^QW z+vweTx_DLm{P-XSBj~Sl<%_b^$=}odJ!S2wAcxenmzFGX1t&Qp8Vxz2VT`uQsQYtdn&_0xVivIcxZ_hnrRtwq4cZSj1c-SG9 z7vHBCA=fd0O1<4*=lu$6pn~_pVKyL@ztw1swbZi0B?spLo56ZKu5;7ZeUml1Ws1?u zqMf1p{5myAzeX$lAi{jIUqo1g4!zWLMm9cfWcnw`k6*BR^?$2(&yW?>w;G$EmTA@a z6?y#K$C~ZT8+v{87n5Dm&H6Pb_EQ@V0IWmG9cG=O;(;5aMWWrIPzz4Q`mhK;qQp~a z+BbQrEQ+w{SeiuG-~Po5f=^EvlouB@_|4xQXH@A~KgpFHrwu%dwuCR)=B&C(y6J4J zvoGk9;lLs9%iA-IJGU#RgnZZR+@{5lYl8(e1h6&>Vc_mvg0d@);X zji4T|n#lB!>pfL|8tQYkw?U2bD`W{na&;*|znjmalA&f;*U++_aBYerq;&C8Kw7mI z7tsG*?7*5j&dU)Lje;^{D_h`%(dK|pB*A*1(Jj)w^mZ9HB|vGLkF1GEFhu&rH=r=8 zMxO42e{Si6$m+Zj`_mXb&w5Q(i|Yxyg?juUrY}78uo@~3v84|8dfgbPd0iQJRdMj< zncCNGdMEcsxu#o#B5+XD{tsg*;j-eF8`mp~K8O1J!Z0+>0=7O=4M}E?)H)ENE;P*F z$Ox?ril_^p0g7xhDUf(q652l|562VFlC8^r8?lQv;TMvn+*8I}&+hIQYh2 z1}uQQaag&!-+DZ@|C+C$bN6W;S-Z@)d1|en+XGvjbOxCa-qAF*LA=6s(Jg+g;82f$ z(Vb)8I)AH@cdjGFAR5Rqd0wiNCu!xtqWbcTx&5kslzTb^7A78~Xzw1($UV6S^VWiP zFd{Rimd-0CZC_Bu(WxBFW7+k{cOW7DxBBkJdJ;VsJ4Z@lERQr%3eVv&$%)b%<~ zCl^Y4NgO}js@u{|o~KTgH}>!* z_iDNqX2(As7T0xivMH|3SC1ivm8Q}6Ffcd7owUKN5lHAtzMM4<0v+ykUT!QiowO;`@%JGv+K$bBx@*S7C8GJVqQ_K>12}M`f_Ys=S zKFh}HM9#6Izb$Y{wYzItTy+l5U2oL%boCJn?R3?jP@n$zSIwlmyGq30Cw4QBO|14` zW5c);AN*J3&eMFAk$SR~2k|&+&Bc$e>s%c{`?d~85S-UWjA>DS5+;UKZ}5oVa5O(N zqqc@>)nee)+4MUjH?FGv%hm2{IlIF-QX}ym-7ok4Z9{V+ZHVZQl$A*x!(q%<2~iVv znUa+BX35&lCb#9VE-~Y^W_f;Xhl%vgjwdjzMy$FsSIj&ok}L+X`4>J=9BkN&nu^E*gbhj3(+D>C4E z@Fwq_=N)^bKFSHTzZk?-gNU$@l}r}dwGyh_fNi=9b|n}J>&;G!lzilbWF4B}BBq4f zYIOl?b)PSh#XTPp4IS5ZR_2C!E)Z`zH0OW%4;&~z7UAyA-X|sh9@~>cQW^COA9hV4 zXcA6qUo9P{bW1_2`eo6%hgbN%(G-F1xTvq!sc?4wN6Q4`e9Hku zFwvlAcRY?6h^Fj$R8zCNEDq8`=uZB8D-xn)tA<^bFFy}4$vA}Xq0jAsv1&5!h!yRA zU()KLJya5MQ`q&LKdH#fwq&(bNFS{sKlEh_{N%{XCGO+po#(+WCLmKW6&5iOHny>g z3*VFN?mx!16V5{zyuMWDVP8U*|BGT$(%IO|)?EF|OI*sq&RovH!N%=>i_c?K*A>>k zyg1+~++zY4Q)J;VWN0axhoIKx;l&G$gvj(#go^pZskEVj8^}is3Jw26LzYYVos0HX zRPvmK$dVxM8(Tc?pHFe0Z3uq){{#OK3i-ra#@+;*=ui8)y6hsRv z4Fxx1c1+fr!VI{L3DFMwXKrfl#Q8hfP@ajgEau&QMCxd{g#!T^;ATXW)nUg&$-n25 zruy3V!!;{?OTobo|0GAxe`Acn3GV@W=&n;~&9 zQM>NWW~R@OYORkJAo+eq1!4vzmf9K%plR4(tB@TR&FSbDoRgJ8qVcH#;7lQub*nq&?Z>7WM=oeEVjkaG zT#f)=o!M2DO5hLR+op>t0CixJCIeXH*+z{-XS|%jx)y(j&}Wo|3!l7{o)HU3m7LYyhv*xF&tq z%IN7N;D4raue&&hm0xM=`qv`+TK@;_xAcGKuK(2|75~ar2Yw)geNLSmVxV@x89bQu zpViVKKnlkwjS&&c|-X6`~xdnh}Ps)Hs z4VbUL^{XNLf7_|Oi>tA%?SG5zax}esF*FH3d(JH^Gvr7Rp*n=t7frH!U;!y1gJB^i zY_M$KL_}mW&XKaDEi9K-wZR|q*L32&m+2n_8lq$xRznJ7p8}V>w+d@?uB!eS3#u<} zIaqi!b!w}a2;_BfUUhGMy#4dPx>)_>yZ`ai?Rk`}d0>~ce-PfY-b?Csd(28yX22L% zI7XI>OjIHYTk_@Xk;Gu^F52^Gn6E1&+?4MxDS2G_#PQ&yXPXP^<-p|2nLTb@AAQEY zI*UQ9Pmm{Kat}wuazpjSyXCdnrD&|C1c5DIb1TnzF}f4KIV6D)CJ!?&l&{T)e4U%3HTSYqsQ zo@zWB1o}ceQSV)<4G<)jM|@@YpL+XHuWsr5AYh^Q{K=wSV99D~4RRU52FufmMBMmd z_H}L#qe(}|I9ZyPRD6kT>Ivj&2Y?qVZq<4bG_co_DP`sE*_Xw8D;+7QR$Uq(rr+u> z8bHUWbV19i#)@@G4bCco@Xb<8u~wVDz9S`#k@ciJtlu@uP1U0X?yov8v9U3VOig2t zL9?n$P3=1U_Emi$#slR>N5wH-=J&T=EdUHA}_Z zZIl3nvMP*AZS9{cDqFanrA~S5BqxtNm9tlu;^`)3X&V4tMAkJ4gEIPl= zoV!Gyx0N{3DpD@)pv^iS*dl2FwANu;1;%EDl}JQ7MbxLMAp>)UwNwe{=V}O-5C*>F zu?Ny+F64jZn<+fKjF01}8h5H_3pey|;%bI;SFg$w8;IC<8l|3#Lz2;mNNik6sVTG3 z+Su^rIE#40C4a-587$U~%KedEEw1%r6wdvoMwpmlXH$xPnNQN#f%Z7|p)nC>WsuO= z4zyqapLS<8(UJ~Qi9d|dQijb_xhA2)v>la)<1md5s^R1N&PiuA$^k|A<+2C?OiHbj z>Bn$~t)>Y(Zb`8hW7q9xQ=s>Rv81V+UiuZJc<23HplI88isqRCId89fb`Kt|CxVIg znWcwprwXnotO>3s&Oypkte^9yJjlUVVxSe%_xlzmje|mYOVPH^vjA=?6xd0vaj0Oz zwJ4OJNiFdnHJX3rw&inskjryukl`*fRQ#SMod5J|KroJRsVXa5_$q7whSQ{gOi*s0 z1LeCy|JBWRsDPn7jCb4s(p|JZiZ8+*ExC@Vj)MF|*Vp{B(ziccSn`G1Br9bV(v!C2 z6#?eqpJBc9o@lJ#^p-`-=`4i&wFe>2)nlPK1p9yPFzJCzBQbpkcR>={YtamIw)3nt z(QEF;+)4`>8^_LU)_Q3 zC5_7lgi_6y>U%m)m@}Ku4C}=l^J=<<7c;99ec3p{aR+v=diuJR7uZi%aQv$oP?dn?@6Yu_+*^>T0ptf(oobdL;6)N-I!TO`zg^Xbv3#L0I~sn@WGk-^SmPh5>W+LB<+1PU}AKa?FCWF|qMNELOgdxR{ zbqE7@jVe+FklzdcD$!(A$&}}H*HQFTJ+AOrJYnhh}Yvta(B zQ_bW4Rr;R~&6PAKwgLWXS{Bnln(vUI+~g#kl{r+_zbngT`Y3`^Qf=!PxN4IYX#iW4 zucW7@LLJA9Zh3(rj~&SyN_pjO8H&)|(v%!BnMWySBJV=eSkB3YSTCyIeJ{i;(oc%_hk{$_l;v>nWSB)oVeg+blh=HB5JSlG_r7@P z3q;aFoZjD_qS@zygYqCn=;Zxjo!?NK!%J$ z52lOP`8G3feEj+HTp@Tnn9X~nG=;tS+z}u{mQX_J0kxtr)O30YD%oo)L@wy`jpQYM z@M>Me=95k1p*FW~rHiV1CIfVc{K8r|#Kt(ApkXKsDG$_>76UGNhHExFCw#Ky9*B-z zNq2ga*xax!HMf_|Vp-86r{;~YgQKqu7%szk8$hpvi_2I`OVbG1doP(`gn}=W<8%Gn z%81#&WjkH4GV;4u43EtSW>K_Ta3Zj!XF?;SO3V#q=<=>Tc^@?A`i;&`-cYj|;^ zEo#Jl5zSr~_V-4}y8pnufXLa80vZY4z2ko7fj>DR)#z=wWuS1$$W!L?(y}YC+yQ|G z@L&`2upy3f>~*IquAjkVNU>}c10(fq#HdbK$~Q3l6|=@-eBbo>B9(6xV`*)sae58*f zym~RRVx;xoCG3`JV`xo z!lFw)=t2Hy)e!IFs?0~7osWk(d%^wxq&>_XD4+U#y&-VF%4z?XH^i4w`TxpF{`XhZ z%G}iEzf!T(l>g;W9<~K+)$g!{UvhW{E0Lis(S^%I8OF&%kr!gJ&fMOpM=&=Aj@wuL zBX?*6i51Qb$uhkwkFYkaD_UDE+)rh1c;(&Y=B$3)J&iJfQSx!1NGgPtK!$c9OtJuu zX(pV$bfuJpRR|K(dp@^j}i&HeJOh@|7lWo8^$*o~Xqo z5Sb+!EtJ&e@6F+h&+_1ETbg7LfP5GZjvIUIN3ibCOldAv z)>YdO|NH$x7AC8dr=<2ekiY1%fN*r~e5h6Yaw<{XIErujKV~tiyrvV_DV0AzEknC- zR^xKM3i<1UkvqBj3C{wDvytOd+YtDSGu!gEMg+!&|8BQrT*|p)(dwQLEy+ zMtMzij3zo40)CA!BKZF~yWg?#lWhqD3@qR)gh~D{uZaJO;{OWV8XZ_)J@r3=)T|kt zUS1pXr6-`!Z}w2QR7nP%d?ecf90;K_7C3d!UZ`N(TZoWNN^Q~RjVhQG{Y<%E1PpV^4 z-m-K+$A~-+VDABs^Q@U*)YvhY4Znn2^w>732H?NRK(5QSS$V@D7yz2BVX4)f5A04~$WbxGOam22>t&uD)JB8-~yiQW6ik;FGblY_I>SvB_z2?PS z*Qm&qbKI{H1V@YGWzpx`!v)WeLT02};JJo*#f$a*FH?IIad-^(;9XC#YTWN6;Z6+S zm4O1KH=#V@FJw7Pha0!9Vb%ZIM$)a`VRMoiN&C|$YA3~ZC*8ayZRY^fyuP6$n%2IU z$#XceYZeqLTXw(m$_z|33I$B4k~NZO>pP6)H_}R{E$i%USGy{l{-jOE;%CloYPEU+ zRFxOn4;7lIOh!7abb23YKD+_-?O z0FP9otcAh+oSj;=f#$&*ExUHpd&e#bSF%#8*&ItcL2H$Sa)?pt0Xtf+t)z$_u^wZi z44oE}r4kIZGy3!Mc8q$B&6JqtnHZ>Znn!Zh@6rgIu|yU+zG8q`q9%B18|T|oN3zMq z`l&D;U!OL~%>vo&q0>Y==~zLiCZk4v%s_7!9DxQ~id1LLE93gf*gg&2$|hB#j8;?3 z5v4S;oM6rT{Y;I+#FdmNw z){d%tNM<<#GN%n9ox7B=3#;u7unZ~tLB_vRZ52a&2=IM)2VkXm=L+Iqq~uk#Dug|x z>S84e+A7EiOY5lj*!q?6HDkNh~0g;0Jy(al!ZHHDtur9T$y-~)94HelX1NHjXWIM7UAe}$?jiz z9?P4`I0JM=G5K{3_%2jPLC^_Mlw?-kYYgb7`qGa3@dn|^1fRMwiyM@Ch z;CB&o7&&?c5e>h`IM;Wnha0QKnEp=$hA8TJgR-07N~U5(>9vJzeoFsSRBkDq=x(YgEMpb=l4TDD`2 zwVJpWGTA_u7}?ecW7s6%rUs&NXD3+n;jB86`X?8(l3MBo6)PdakI6V6a}22{)8ilT zM~T*mU}__xSy|6XSrJ^%lDAR3Lft%+yxC|ZUvSO_nqMX!_ul3;R#*{~4DA=h$bP)%8Yv9X zyp><|e8=_ttI}ZAwOd#dlnSjck#6%273{E$kJuCGu=I@O)&6ID{nWF5@gLb16sj|&Sb~+du4e4O_%_o`Ix4NRrAsyr1_}MuP94s>de8cH-OUkVPk3+K z&jW)It9QiU-ti~AuJkL`XMca8Oh4$SyJ=`-5WU<{cIh+XVH#e4d&zive_UHC!pN>W z3TB;Mn5i)9Qn)#6@lo4QpI3jFYc0~+jS)4AFz8fVC;lD^+idw^S~Qhq>Tg(!3$yLD zzktzoFrU@6s4wwCMz}edpF5i5Q1IMmEJQHzp(LAt)pgN3&O!&d?3W@6U4)I^2V{;- z6A(?zd93hS*uQmnh4T)nHnE{wVhh(=MMD(h(P4+^p83Om6t<*cUW>l(qJzr%5vp@K zN27ka(L{JX=1~e2^)F^i=TYj&;<7jyUUR2Bek^A8+3Up*&Xwc{)1nRR5CT8vG>ExV zHnF3UqXJOAno_?bnhCX-&kwI~Ti8t4`n0%Up>!U`ZvK^w2+0Cs-b9%w%4`$+To|k= zKtgc&l}P`*8IS>8DOe?EB84^kx4BQp3<7P{Pq}&p%xF_81pg!l2|u=&I{AuUgmF5n zJQCTLv}%}xbFGYtKfbba{CBo)lWW%Z>i(_NvLhoQZ*5-@2l&x>e+I~0Nld3UI9tdL zRzu8}i;X!h8LHVvN?C+|M81e>Jr38%&*9LYQec9Ax>?NN+9(_>XSRv&6hlCYB`>Qm z1&ygi{Y()OU4@D_jd_-7vDILR{>o|7-k)Sjdxkjgvi{@S>6GqiF|o`*Otr;P)kLHN zZkpts;0zw_6;?f(@4S1FN=m!4^mv~W+lJA`&7RH%2$)49z0A+8@0BCHtj|yH--AEL z0tW6G%X-+J+5a{5*WKaM0QDznf;V?L5&uQw+yegDNDP`hA;0XPYc6e0;Xv6|i|^F2WB)Z$LR|HR4 zTQsRAby9(^Z@yATyOgcfQw7cKyr^3Tz7lc7+JEwwzA7)|2x+PtEb>nD(tpxJQm)Kn zW9K_*r!L%~N*vS8<5T=iv|o!zTe9k_2jC_j*7ik^M_ zaf%k{WX{-;0*`t`G!&`eW;gChVXnJ-Rn)To8vW-?>>a%QU1v`ZC=U)f8iA@%JG0mZ zDqH;~mgBnrCP~1II<=V9;EBL)J+xzCoiRBaeH&J6rL!{4zIY8tZka?_FBeQeNO3q6 zyG_alW54Ba&wQf{&F1v-r1R6ID)PTsqjIBc+5MHkcW5Fnvi~{-FjKe)t1bl}Y;z@< z=!%zvpRua>>t_x}^}z0<7MI!H2v6|XAyR9!t50q-A)xk0nflgF4*OQlCGK==4S|wc zRMsSscNhRzHMBU8TdcHN!q^I}x0iXJ%uehac|Zs_B$p@CnF)HeXPpB_Za}F{<@6-4 zl%kml@}kHQ(ypD8FsPJ2=14xXJE|b20RUIgs!2|R3>LUMGF6X*B_I|$`Qg=;zm7C z{mEDy9dTmPbued7mlO@phdmAmJ7p@GR1bjCkMw6*G7#4+`k>fk1czdJUB!e@Q(~6# zwo%@p@V5RL0ABU2LH7Asq^quDUho@H>eTZH9f*no9fY0T zD_-9px3e}A!>>kv5wk91%C9R1J_Nh!*&Kk$J3KNxC}c_@zlgpJZ+5L)Nw|^p=2ue}CJtm;uj*Iqr)K})kA$xtNUEvX;4!Px*^&9T_`IN{D z{6~QY=Nau6EzpvufB^hflc#XIsSq0Y9(nf$d~6ZwK}fal92)fr%T3=q{0mP-EyP_G z)UR5h@IX}3Qll2b0oCAcBF>b*@Etu*aTLPU<%C>KoOrk=x?pN!#f_Og-w+;xbFgjQ zXp`et%lDBBh~OcFnMKMUoox0YwBNy`N0q~bSPh@+enQ=4RUw1) zpovN`QoV>vZ#5LvC;cl|6jPr}O5tu!Ipoyib8iXqy}TeJ;4+_7r<1kV0v5?Kv>fYp zg>9L`;XwXa&W7-jf|9~uP2iyF5`5AJ`Q~p4eBU$MCC00`rcSF>`&0fbd^_eqR+}mK z4n*PMMa&FOcc)vTUR zlDUAn-mh`ahi_`f`=39JYTNVjsTa_Y3b1GOIi)6dY)D}xeshB0T8Eov5%UhWd1)u}kjEQ|LDo{tqKKrYIfVz~@dp!! zMOnah@vp)%_-jDTUG09l+;{CkDCH|Q{NqX*uHa1YxFShy*1+;J`gywKaz|2Q{lG8x zP?KBur`}r`!WLKXY_K;C8$EWG>jY3UIh{+BLv0=2)KH%P}6xE2kg)%(-uA6lC?u8}{K(#P*c zE9C8t*u%j2r_{;Rpe1A{9nNXU;b_N0vNgyK!EZVut~}+R2rcbsHilqsOviYh-pYX= zHw@53nlmwYI5W5KP>&`dBZe0Jn?nAdC^HY1wlR6$u^PbpB#AS&5L6zqrXN&7*N2Q` z+Rae1EwS)H=aVSIkr8Ek^1jy2iS2o7mqm~Mr&g5=jjt7VxwglQ^`h#Mx+x2v|9ZAwE$i_9918MjJxTMr?n!bZ6n$}y11u8I9COTU`Z$Fi z!AeAQLMw^gp_{+0QTEJrhL424pVDp%wpku~XRlD3iv{vQ!lAf!_jyqd_h}+Tr1XG| z`*FT*NbPqvHCUsYAkFnM`@l4u_QH&bszpUK#M~XLJt{%?00GXY?u_{gj3Hvs!=N(I z(=AuWPijyoU!r?aFTsa8pLB&cx}$*%;K$e*XqF{~*rA-qn)h^!(-;e}O#B$|S~c+U zN4vyOK0vmtx$5K!?g*+J@G1NmlEI=pyZXZ69tAv=@`t%ag_Hk{LP~OH9iE)I= zaJ69b4kuCkV0V zo(M0#>phpQ_)@j;h%m{-a*LGi(72TP)ws2w*@4|C-3+;=5DmC4s7Lp95%n%@Ko zfdr3-a7m*dys9iIci$A=4NPJ`HfJ;hujLgU)ZRuJI`n;Pw|yksu!#LQnJ#dJysgNb z@@qwR^wrk(jbq4H?d!lNyy72~Dnn87KxsgQ!)|*m(DRM+eC$wh7KnS-mho3|KE)7h zK3k;qZ;K1Lj6uEXLYUYi)1FN}F@-xJ z@@3Hb84sl|j{4$3J}aTY@cbX@pzB_qM~APljrjju6P0tY{C@ zpUCOz_NFmALMv1*blCcwUD3?U6tYs+N%cmJ98D%3)%)Xu^uvzF zS5O!sc#X6?EwsYkvPo6A%O8&y8sCCQH<%f2togVwW&{M;PR!a(ZT_A+jVAbf{@5kL zB@Z(hb$3U{T_}SKA_CoQVU-;j>2J=L#lZ~aQCFg-d<9rzs$_gO&d5N6eFSc z1ml8)P*FSi+k@!^M9nDWR5e@ATD8oxtDu=36Iv2!;dZzidIS(PCtEuXAtlBb1;H%Z zwnC^Ek*D)EX4#Q>R$$WA2sxC_t(!!6Tr?C#@{3}n{<^o;9id1RA&-Pig1e-2B1XpG zliNjgmd3c&%A}s>qf{_j#!Z`fu0xIwm4L0)OF=u(OEmp;bLCIaZX$&J_^Z%4Sq4GZ zPn6sV_#+6pJmDN_lx@1;Zw6Md_p0w9h6mHtzpuIEwNn>OnuRSC2=>fP^Hqgc)xu^4 z<3!s`cORHJh#?!nKI`Et7{3C27+EuH)Gw1f)aoP|B3y?fuVfvpYYmmukx0ya-)TQX zR{ggy5cNf4X|g)nl#jC9p>7|09_S7>1D2GTRBUTW zAkQ=JMRogZqG#v;^=11O6@rPPwvJkr{bW-Qg8`q8GoD#K`&Y+S#%&B>SGRL>;ZunM@49!}Uy zN|bBCJ%sO;@3wl0>0gbl3L@1^O60ONObz8ZI7nder>(udj-jt`;yj^nTQ$L9`OU9W zX4alF#$|GiR47%x@s&LV>2Sz2R6?;2R~5k6V>)nz!o_*1Y!$p>BC5&?hJg_MiE6UBy>RkVZj`9UWbRkN-Hk!S`=BS3t3uyX6)7SF#)71*}`~Ogz z1rap5H6~dhBJ83;q-Y<5V35C2&F^JI-it(=5D#v!fAi9p#UwV~2tZQI+W(Dv?1t9? zfh*xpxxO{-(VGB>!Q&0%^YW_F!@aZS#ucP|YaD#>wd1Fv&Z*SR&mc;asi}1G) z_H>`!akh-Zxq9#io(7%;a$)w+{QH)Y$?UK1Dt^4)up!Szcxnu}kn$0afcfJL#IL+S z5gF_Y30j;{lNrG6m~$Ay?)*V9fZuU@3=kd40=LhazjFrau>(Y>SJNtOz>8x_X-BlA zIpl{i>OarVGj1v(4?^1`R}aQB&WCRQzS~;7R{tDZG=HhgrW@B`W|#cdyj%YBky)P= zpxuOZkW>S6%q7U{VsB#G(^FMsH5QuGXhb(sY+!-R8Bmv6Sx3WzSW<1MPPN1!&PurYky(@`bP9tz z52}LH9Q?+FF5jR6-;|+GVdRA!qtd;}*-h&iIw3Tq3qF9sDIb1FFxGbo&fbG5n8$3F zyY&PWL{ys^dTO}oZ#@sIX^BKW*bon=;te9j5k+T%wJ zNJtoN1~YVj4~YRrlZl)b&kJqp+Z`DqT!la$x&&IxgOQw#yZd-nBP3!7FijBXD|IsU8Zl^ zc6?MKpJQ+7ka|tZQLfchD$PD|;K(9FiLE|eUZX#EZxhG!S-63C$jWX1Yd!6-Yxi-u zjULIr|0-Q%D9jz}IF~S%>0(jOqZ(Ln<$9PxiySr&2Oic7vb<8q=46)Ln%Z|<*z5&> z3f~Zw@m;vR(bESB<=Jqkxn(=#hQw42l(7)h`vMQQTttz9XW6^|^8EK7qhju4r_c*b zJIi`)MB$w@9epwdIfnEBR+?~);yd6C(LeMC& zn&&N*?-g&BBJcV;8&UoZi4Lmxcj16ojlxR~zMrf=O_^i1wGb9X-0@6_rpjPYemIin zmJb+;lHe;Yp=8G)Q(L1bzH*}I>}uAqhj4;g)PlvD9_e_ScR{Ipq|$8NvAvLD8MYr}xl=bU~)f%B3E>r3Bu9_t|ThF3C5~BdOve zEbk^r&r#PT&?^V1cb{72yEWH}TXEE}w>t!cY~rA+hNOTK8FAtIEoszp!qqptS&;r$ zaYV-NX96-h$6aR@1xz6_E0^N49mU)-v#bwtGJm)ibygzJ8!7|WIrcb`$XH~^!a#s& z{Db-0IOTFq#9!^j!n_F}#Z_nX{YzBK8XLPVmc&X`fT7!@$U-@2KM9soGbmOSAmqV z{nr$L^MBo_u^Joyf0E^=eo{Rt0{{e$IFA(#*kP@SQd6lWT2-#>` zP1)7_@IO!9lk>Zt?#CU?cuhiLF&)+XEM9B)cS(gvQT!X3`wL*{fArTS;Ak`J<84du zALKPz4}3nlG8Fo^MH0L|oK2-4xIY!~Oux~1sw!+It)&D3p;+N8AgqKI`ld6v71wy8I!eP0o~=RVcFQR2Gr(eP_JbSytoQ$Yt}l*4r@A8Me94y z8cTDWhqlq^qoAhbOzGBXv^Wa4vUz$(7B!mX`T=x_ueKRRDfg&Uc-e1+z4x$jyW_Pm zp?U;-R#xt^Z8Ev~`m`iL4*c#65Nn)q#=Y0l1AuD&+{|8-Gsij3LUZXpM0Bx0u7WWm zH|%yE@-#XEph2}-$-thl+S;__ciBxSSzHveP%~v}5I%u!z_l_KoW{KRx2=eB33umE zIYFtu^5=wGU`Jab8#}cnYry@9p5UE#U|VVvx_4l49JQ;jQdp(uw=$^A$EA$LM%vmE zvdEOaIcp5qX8wX{mYf0;#51~imYYPn4=k&#DsKTxo{_Mg*;S495?OBY?#gv=edYC* z^O@-sd-qa+U24xvcbL0@C7_6o!$`)sVr-jSJE4XQUQ$?L7}2(}Eixqv;L8AdJAVqc zq}RPgpnDb@E_;?6K58r3h4-!4rT4Ab#rLHLX?eMOfluJk=3i1@Gt1i#iA=O`M0@x! z(HtJP9BMHXEzuD93m|B&woj0g6T?f#^)>J>|I4C5?Gam>n9!8CT%~aT;=oco5d6U8 zMXl(=W;$ND_8+DD*?|5bJ!;8ebESXMUKBAf7YBwNVJibGaJ*(2G`F%wx)grqVPjudiaq^Kl&g$8A2 zWMxMr@_$c}d+;_B`#kUX-t|4VKH&_f^^EP0&=DPLW)H)UzBG%%Tra*5 z%$kyZe3I&S#gfie^z5)!twG={3Cuh)FdeA!Kj<-9** zvT*5%Tb`|QbE!iW-XcOuy39>D3oe6x{>&<#E$o8Ac|j)wq#kQzz|ATd=Z0K!p2$QE zPu?jL8Lb^y3_CQE{*}sTDe!2!dtlFjq&YLY@2#4>XS`}v#PLrpvc4*@q^O{mmnr5D zmyJq~t?8>FWU5vZdE(%4cuZuao0GNjp3~Dt*SLaxI#g_u>hu@k&9Ho*#CZP~lFJHj z(e!SYlLigyc?&5-YxlE{uuk$9b&l6d`uIlpg_z15dPo*iU&|Khx2*A5Fp;8iK_bdP z?T6|^7@lcx2j0T@x>X7|kuuBSB7<^zeY~R~4McconTxA2flHC0_jFxmSTv-~?zVT| zG_|yDqa9lkF*B6_{j=T>=M8r<0s;@z#h)3BQ4NLl@`Xr__o7;~M&dL3J8fP&zLfDfy z);ckcTev{@OUlZ`bCo(-3? z1u1xD`PKgSg?RqeVVsF<1SLF;XYA@Bsa&cY!I48ZJn1V<3d!?s=St?TLo zC0cNr`qD*M#s6f~X>SCNVkva^9A2ZP>CoJ9bvgXe_c}WdX-)pHM5m7O zrHt#g$F0AO+nGA;7dSJ?)|Mo~cf{z2L)Rz!`fpi73Zv)H=a5K)*$5sf_IZypi($P5 zsPwUc4~P-J1@^3C6-r9{V-u0Z&Sl7vNfmuMY4yy*cL>_)BmQF!8Om9Dej%cHxbIzA zhtV0d{=%cr?;bpBPjt@4w=#<>k5ee=TiWAXM2~tUGfm z$s&!Dm0R^V$}fOR*B^kGaipi~rx~A2cS0;t&khV1a4u38*XRUP~f za!rZMtay8bsLt6yFYl@>-y^31(*P!L^^s@mslZy(SMsv9bVoX`O#yBgEcjCmGpyc* zeH$Dw6vB5P*;jor+JOX@;6K#+xc)Z9B8M=x2a@Wx-{snPGpRmOC$zpsqW*JCh@M2Y z#K+M(>=#d^>Of9C`))h<=Bsy)6zaMJ&x-t%&+UcpLjV`jo4R2025 zXaG8EA!0lQa)|dx-@{O)qP6`$rhCkoQqZ`^SW8g-kOwrwsK8 z3ms*AIcyj}-1x&A&vSq{r=QMyp3CHdWH35!sad#!Sm>^|-|afB+Q;|Iq@LFgqIp#Z zD1%H+3I?6RGnk&IFo|u+E0dCxXz4yI^1i!QTu7uvIEH>i3rR{srcST`LIRwdV1P;W z+%AN1NIf@xxvVLiSX`8ILA8MzNqE&7>%jMzGt9wm78bo9<;h*W84i29^w!>V>{N+S zd`5Zmz^G;f=icvoOZfK5#1ctx*~UwD=ab4DGQXehQ!XYnak*dee%YN$_ZPL%KZuz$ zD;$PpT;HM^$KwtQm@7uvT`i6>Hae1CoRVM2)NL<2-k2PiX=eAx+-6j#JI?M}(tuBW zkF%jjLR)O`gI2fcPBxF^HeI|DWwQWHVR!;;{BXXHskxh8F@BMDn`oEi-NHt;CLymW z=KSv5)3dyzec0T5B*`g-MQ<;gz=nIWKUi9ko<|4I(-E0k$QncH>E4l z**1w&#={&zv4Tvhgz#c29`m|;lU-jmaXFMC11 z*dlXDMEOG>VoLMc>!rApwOu2prKSi*!w%`yzGmS+k(zm*CsLK*wv{S_0WX^8A-rKy zbk^Gf_92^7iB_uUF)EE+ET4d|X|>d&mdN?x@vxKAQk`O+r4Qdu>XGy(a(19g;=jU} zFX{O*_NG>!$@jh!U369Lnc+D~qch3uT+_Amyi}*k#LAAwh}k8IPK5a-WZ81ufD>l> z$4cF}GSz>ce`3FAic}6W4Z7m9KGO?(eWqi@L|5Hq0@L|&2flN1PVl}XgQ2q*_n2s3 zt5KtowNkTYB5b;SVuoXA@i5irXO)A&%7?V`1@HGCB&)Wgk+l|^XXChq;u(nyPB}b3 zY>m5jkxpZgi)zfbgv&ec4Zqdvm+D<?Im*mXweS9H+V>)zF#Zp3)bhl$PbISY{5=_z!8&*Jv~NYtI-g!>fDs zmvL5O^U%!^VaKA9gvKw|5?-jk>~%CVGvctKmP$kpnpfN{D8@X*Aazi$txfa%vd-|E z>kYmV66W!lNekJPom29LdZ%(I+ZLZYTXzTg*to~m?7vp%{V<~>H+2}PQ?PPAq`36R z<%wR8v6UkS>Wt#hzGk#44W<%9S=nBfB);6clKwnxY}T*w21Qc3_?IJ@4gYzC7s;WP zVQNI(M=S=JT#xsZy7G`cR(BP9*je0bfeN8JN5~zY(DDs0t{LpHOIbN);?T-69Pf3R zSNe*&p2%AwXHL>__g+xd4Hlc_vu<25H?(`nafS%)3UPP7_4;gk-9ckt8SJRTv5v0M z_Hww`qPudL?ajIR&X*;$y-`<)6dxx1U~5eGS13CB!lX;3w7n&lDDiArbAhSycd}+b zya_3p@A`$kQy;|NJZ~s44Hqo7Hwt}X86NK=(ey>lgWTtGL6k@Gy;PbO!M%1~Wcn2k zUFP|*5d>t-X*RU8g%>|(wwj*~#l4z^Aatf^DWd1Wj#Q*AY0D^V@sC`M zjJc6qXu0I7Y*2;;gGu!plAFzG=J;1%eIOdn zQA>J&e05UN*7I5@yRhK|lbBSfJ+5Uq;!&HV@xfPZrgD}kE*1DSq^=%{o%|LChhl#0 zlMb<^a6ixzpd{kNZr|3jTGeEzuo}-eLT-)Q$#b{!vKx8Tg}swCni>{#%vDY$Ww$84 zew3c9BBovqb}_&BRo#^!G(1Eg((BScRZ}C)Oz?y`T5wOrv);)b^4XR8 zhJo7+<^7)qB>I;46!GySzdneZ>n_E1oWZY;kf94#)s)kWjuJN1c+wbVoNQcmnv}{> zN0pF+Sl3E}UQ$}slSZeLJrwT>Sr}#V(dVaezCQl2|4LN`7L7v&siYR|r7M(*JYfR$ zst3=YaDw$FSc{g}KHO&QiKxuhEzF{f%RJLKe3p*7=oo`WNP)M(9X1zIQPP0XHhY3c znrP{$4#Ol$A0s|4S7Gx2L23dv*Gv2o;h((XVn+9+$qvm}s%zi6nI-_s6?mG! zj{DV;qesJb&owKeEK?=J>UcAlYckA7Sl+I&IN=yasrZOkejir*kE@SN`fk<8Fgx*$ zy&fE6?}G)d_N`){P~U@1jRVA|2*69)KSe_}!~?+`Yb{Y=O~_+@!j<&oVQQMnhoIRU zA0CyF1OFfkK44n*JD~!2!SCPM;PRSk%1XL=0&rz00wxPs&-_eapJy#$h!eqY%nS0{ z!aGg58JIJPF3_ci%n)QSVpa2H`vIe$RD43;#IRfDV&Ibit z+?>HW4{2wOfC6Fw)}4x}i1maDxcE1qi@BS*qcxD2gE@h3#4cgU*D-&3z7D|tVZWt= z-Cy2+*Cm@P4GN_TPUtaVyVesbVDazF@)j8VJ4>XZv!f%}&eO1SvIgr}4`A*3#vat< z_MoByL(qW6L7SFZ#|Gc1fFN)L2PxY+{B8tJp+pxRyz*87)vXR}*=&ahXjBlQKguuf zX6x<<6fQulE^C*KH8~W%ptpaC0l?b=_{~*U4?5Vt;dgM4t_{&UZ1C2j?b>b+5}{IF_CUyvz-@QZPMlJ)r_tS$9kH%RPv#2_nMb zRLj5;chJ72*U`Z@Dqt4$@_+k$%|8m(HqLG!qT4P^DdfvGf&){gKnGCX#H0!;W=AGP zbA&Z`-__a)VTS}kKFjWGk z%|>yE?t*EJ!qeQ%dPk$;xIQ+P0;()PCBDgjJm6Buj{f^awNoVx+9<|lg3%-$G(*f) zll6oOkN|yamn1uyl2*N-lnqRI1cvs_JxLTeahEK=THV$Sz*gQhKNb*p0fNoda#-&F zB-qJgW^g}!TtM|0bS2QZekW7_tKu%GcJ!4?lObt0z_$mZ4rbQ0o=^curCs3bJK6sq z9fu-aW-l#>z~ca(B;4yv;2RZ?tGYAU)^)Kz{L|4oPj zdOf_?de|#yS)p2v8-N||+XL=O*%3+y)oI(HbM)Ds?q8~HPzIP(vs*G`iddbWq}! z(2!VjP&{Z1w+%eUq^ Date: Thu, 13 Feb 2025 17:56:09 -0600 Subject: [PATCH 04/32] One more file type to ignore and remove --- .gitignore | 1 + .../.gradle/7.5.1/checksums/checksums.lock | Bin 17 -> 0 bytes .../dependencies-accessors.lock | Bin 17 -> 0 bytes .../7.5.1/executionHistory/executionHistory.lock | Bin 17 -> 0 bytes .../.gradle/7.5.1/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 0 bytes 6 files changed, 1 insertion(+) delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/checksums/checksums.lock delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.lock delete mode 100755 2022-2023/main-bot/.gradle/7.5.1/fileHashes/fileHashes.lock delete mode 100755 2022-2023/main-bot/.gradle/buildOutputCleanup/buildOutputCleanup.lock diff --git a/.gitignore b/.gitignore index ce577a8..a0a205e 100644 --- a/.gitignore +++ b/.gitignore @@ -185,3 +185,4 @@ replay_pid* *.DS_Store *.dat *.hoot +*.lock \ No newline at end of file diff --git a/2022-2023/main-bot/.gradle/7.5.1/checksums/checksums.lock b/2022-2023/main-bot/.gradle/7.5.1/checksums/checksums.lock deleted file mode 100755 index 985a5eed2a29329f107fa90f728c9aa4c690ac93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 VcmZRsSIN6^bXo6A1~6dn1^_oE1rPuL diff --git a/2022-2023/main-bot/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock b/2022-2023/main-bot/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock deleted file mode 100755 index 4e31113a415abeeafe4fc7cf00412199b2f1cff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZRM_&Hf+4QKEh1}FdkGByMN diff --git a/2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.lock b/2022-2023/main-bot/.gradle/7.5.1/executionHistory/executionHistory.lock deleted file mode 100755 index a8b1257178446dd68d7f0fe37d41fff4c9f10cd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 VcmZRczP~U Date: Thu, 13 Feb 2025 20:21:15 -0600 Subject: [PATCH 05/32] Edited some of the auxillary controls --- .../src/main/java/frc/robot/RobotContainer.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index bc366ed..4cc1424 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -19,9 +19,13 @@ * LB - Reset Field Centric Seed * * Auxiliary Controller: - * A - Algae Bar Spin Fast - * B - Algae Bar Spit Out - * Y - Algae Bar Spin Slow + * A - Level 1 Elevator + Coral Arm + * B - Level 2 Elevator + Coral Arm + * Y - Level 3 Elevator + Coral Arm + * X - Level 4 Elevator + Coral Arm + * Thumbpad Down - Algae Bar Intake + * Thumbpad Up - Algae Bar Spit Out + * Thumbpad left - Algae Bar Spin Slow * LB - Elevator Level 1 * RB - Elevator Level 2 * LT - Elevator Level 3 @@ -139,9 +143,9 @@ private void configureBindings(double random) { m_auxillaryController.leftTrigger().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left trigger pressed, move to level 3. Else, go to top // Algae Arm Subsystem - m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop - m_auxillaryController.b().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop - m_auxillaryController.y().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop + m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop + m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop + m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop //m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); //This command was taken out due to clashing and we already have a command that does the same thing From 6f941a169ef5a0d61538710d2caf4a34314d89c3 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 15 Feb 2025 12:30:26 -0600 Subject: [PATCH 06/32] Test Bed code - H --- .../main/java/frc/robot/RobotContainer.java | 292 +++++------------- .../robot/subsystems/ElevatorSubsystem.java | 140 ++++----- 2 files changed, 139 insertions(+), 293 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 4cc1424..68d2ae7 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -2,242 +2,100 @@ // 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. -/* - * This Is The Input List. Whenever Adding A New Input, Make Sure To Add It To The List Below - * - * Driver Controller: - * A - Brake - * B - Point - * Back + Y - SysId Dynamic Forward - * Back + X - SysId Dynamic Reverse - * Start + Y - SysId Quasistatic Forward - * Start + X - SysId Quasistatic Reverse - * DPad Up - Coral Arm Up - * DPad Down - Coral Arm Down - * DPad Center - Coral Arm Stop - * Right Stick - Coral Arm Emergency Stop - * LB - Reset Field Centric Seed - * - * Auxiliary Controller: - * A - Level 1 Elevator + Coral Arm - * B - Level 2 Elevator + Coral Arm - * Y - Level 3 Elevator + Coral Arm - * X - Level 4 Elevator + Coral Arm - * Thumbpad Down - Algae Bar Intake - * Thumbpad Up - Algae Bar Spit Out - * Thumbpad left - Algae Bar Spin Slow - * LB - Elevator Level 1 - * RB - Elevator Level 2 - * LT - Elevator Level 3 - */ - package frc.robot; -import static edu.wpi.first.units.Units.*; - -import com.ctre.phoenix6.swerve.SwerveModule.DriveRequestType; -import com.pathplanner.lib.auto.AutoBuilder; -import com.pathplanner.lib.auto.NamedCommands; -import com.pathplanner.lib.commands.PathPlannerAuto; -import com.ctre.phoenix6.swerve.SwerveRequest; -import java.lang.Math; -import frc.robot.Constants.OperatorConstants; -import frc.robot.generated.TunerConstants; -import frc.robot.commands.Autos; -import frc.robot.commands.ExampleCommand; -import frc.robot.subsystems.ElevatorSubsystem; -import frc.robot.subsystems.ExampleSubsystem; -import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; -import frc.robot.subsystems.ElevatorSubsystem.ElevatorLevels; -import frc.robot.subsystems.AlgaeArmSubsystem; -import frc.robot.subsystems.CommandSwerveDrivetrain; -import frc.robot.subsystems.CoralArmSubsystem; import edu.wpi.first.wpilibj2.command.Command; -import edu.wpi.first.wpilibj2.command.button.CommandXboxController; -import edu.wpi.first.wpilibj2.command.button.CommandGenericHID; -import edu.wpi.first.wpilibj2.command.button.Trigger; -import edu.wpi.first.math.geometry.Rotation2d; -import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Commands; -import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; - -/** little secret comment OwO - * 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. - */ +import edu.wpi.first.wpilibj2.command.FunctionalCommand; +import edu.wpi.first.wpilibj2.command.RunCommand; +import edu.wpi.first.wpilibj2.command.Subsystem; +// import edu.wpi.first.wpilibj2.command.StartEndCommand; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; +// import frc.robot.subsystems.TestSubsystem; +// import frc.robot.subsystems.ElevatorSubsystem; +import frc.robot.subsystems.ElevatorSubsystem; +import frc.robot.subsystems.ElevatorSubsystem.ElevatorPreset; + +// import edu.wpi.first.wpilibj2. + + public class RobotContainer { - // The robot's subsystems and commands are defined here... - private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); - private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( - Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); - private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem(Constants.AlgaeArmConstants.algaeArmBarID); - private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); - private final CommandXboxController m_driverController = - new CommandXboxController(OperatorConstants.kDriverControllerPort); - - private final SendableChooser autoChooser; - /* Swerve drive platform setup */ // From Swerve Project Generator - private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed - private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max angular velocity - - /* Setting up bindings for necessary control of the swerve drive platform */ - private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric() - .withDeadband(MaxSpeed * 0.1).withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband - .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors - private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); - private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); + // Controllers + CommandXboxController driveController = new CommandXboxController(0); + CommandXboxController auxController = new CommandXboxController(1); + + //Subsystems + // TestSubsystem testSubsystem = new TestSubsystem(); + ElevatorSubsystem elevatorSubsystem = new ElevatorSubsystem(10, 11); + + // Variables + int count = 0; - private final Telemetry logger = new Telemetry(MaxSpeed); + // constructor + public RobotContainer() { + configureBindings(); + } - private final CommandXboxController m_auxillaryController = new CommandXboxController(Constants.OperatorConstants.kAuxiliaryControllerPort); + private void configureBindings() { - public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + // auxController.leftTrigger().whileTrue(elevatorSubsystem.commandVoltage(0.1)).onFalse(elevatorSubsystem.commandVoltage(0.0)); + // auxController.rightTrigger().whileTrue(elevatorSubsystem.commandVoltage(-0.1)).onFalse(elevatorSubsystem.commandVoltage(0.0)); - // End of Swerve Drive Platform setup + auxController.leftTrigger().onTrue(new RunCommand(() -> elevatorSubsystem.setMotorElevatorSpeed(0.1) , elevatorSubsystem)); + auxController.rightTrigger().onTrue(new RunCommand(() -> elevatorSubsystem.setMotorElevatorSpeed(-0.1) , elevatorSubsystem)); - /** The container for the robot. Contains subsystems, OI devices, and commands. */ - public RobotContainer() { - + auxController.a().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level1), elevatorSubsystem)); + auxController.b().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level2), elevatorSubsystem)); + auxController.x().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level3), elevatorSubsystem)); + auxController.y().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level4), elevatorSubsystem)); - // Build an auto chooser. This will use Commands.none() as the default option. - autoChooser = AutoBuilder.buildAutoChooser(); - // Another option that allows you to specify the default auto by its name - // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); - SmartDashboard.putData("Auto Chooser", autoChooser); - // Register named commands for use in autonomous routines - // NamedCommands.registerCommand("Example Command", m_exampleSubsystem.exampleMethodCommand()); - // NamedCommands.registerCommand("MoveElevatorToPresets", m_elevatorSubsystem.commandMoveToPreset(null)); - - // Configure the trigger bindings - configureBindings(Math.random()); - } - /** - * 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}. - * @param random TODO - */ - private void configureBindings(double random) { - - // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is pressed, - // cancelling on release. - // m_auxiliaryController.axisLessThan(2, .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about holding it in place? - // m_auxiliaryController.axisGreaterThan(3, .4).whileTrue(m_elevatorSubsystem.raiseElevator()); - - - // Elevator Subsystem - //The commands below make the elevator go to certain levels - m_auxillaryController.leftBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left bumper pressed, move to level 1. Else, go to top - m_auxillaryController.rightBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If right bumper pressed, move to level 2. Else, go to top - m_auxillaryController.leftTrigger().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left trigger pressed, move to level 3. Else, go to top - - // Algae Arm Subsystem - m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop - m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop - m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop - //m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) - // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); - //This command was taken out due to clashing and we already have a command that does the same thing - - // Command Compositions for Elevator + Coral - m_auxillaryController.a().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.b().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.y().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.x().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) + + + + + + + + + + + + + + + + + // Test Commands + + /* + controller.a().onTrue(testSubsystem.testCommand()); + + controller.b().onTrue(new RunCommand(() -> testSubsystem.testMethod1(), testSubsystem)); + + controller.x().onTrue(Commands.startEnd( + () -> testSubsystem.testMethod2(), + () -> testSubsystem.testMethod3(), + testSubsystem)); + + controller.y().onTrue( + new FunctionalCommand( + () -> testSubsystem.testMethod0(), // prints something once it runs + () -> count++, //increments count variable + interrupted -> testSubsystem.testMethod3(), + () -> count >= 10, + testSubsystem) ); + */ - // Coral Arm Subsystem - m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));//Makes Coral Arm Go Up - m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));//Makes Coral Arm Go Down - m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));//Stops Coral Arm - m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());//Emergency Stop for Coral Arm(in case it goes past the top or bottom) Note: I don't know if the onTrue method will only run once, so test it before you use it - - - // Drive Subsystem - // Code below is from swerve drive project generator - - // Note that X is defined as forward according to WPILib convention, - // and Y is defined as to the left according to WPILib convention. - drivetrain.setDefaultCommand( - // Drivetrain will execute this command periodically - drivetrain.applyRequest(() -> - drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward with negative Y (forward) - .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) - .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with negative X (left) - ) - ); - m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); - m_driverController.b().whileTrue(drivetrain.applyRequest(() -> - point.withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())) - )); - - // Run SysId routines when holding back/start and X/Y. - // Note that each routine should be run exactly once in a single log. - m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); - m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); - m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); - m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); - - // reset the field-centric heading on left bumper press - m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); - - drivetrain.registerTelemetry(logger::telemeterize); - - } - /** - * 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); - return autoChooser.getSelected(); + return Commands.print("No autonomous command configured"); + + } } -//another why not comment :) -// I do love some good comments :D \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 785a867..785341f 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -1,11 +1,11 @@ package frc.robot.subsystems; + import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.Command; //import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.Encoder; - -import java.util.function.BooleanSupplier; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; //import com.ctre.phoenix.Util; import com.ctre.phoenix6.hardware.TalonFX; @@ -24,63 +24,60 @@ public class ElevatorSubsystem extends SubsystemBase {//makes elevator subsystem private final Encoder m_Encoder1; // private TitanQuadEncoder m_elevatorEncoder; private double m_elevatorEncoder; // Placeholder - - private ElevatorLevels currentElevatorState; - + // Constants + private double motorElevatorSpeed; public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {//this is da place where stuff is actually initialized m_elevatorEncoder = 0; // Placeholder + motorElevatorSpeed = ElevatorConstants.kMotorElevatorSpeed; + m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); - m_elevatorTopLimitSwitch = new DigitalInput(1); - m_elevatorBottomLimitSwitch = new DigitalInput(2); + m_elevatorTopLimitSwitch = new DigitalInput(7); + m_elevatorBottomLimitSwitch = new DigitalInput(8); m_Encoder1 = new Encoder(0, 1); - currentElevatorState = ElevatorLevels.Lowest; } -public enum ElevatorLevels { - Lowest, // bottom - Level1, // Levels of the reef +public enum ElevatorPreset { + Level1, Level2, Level3, - Level4, - Highest // top + Level4 } + public Command raiseElevator() { + // Placeholder + return runOnce( + () -> { + /* one-time action goes here */ + setMotorElevatorSpeed(0.1); + }); + } -//The commands below that are commented are currently not being used and have been replaced. They are kept just cause - - //public Command raiseElevator() { - // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(0.1); - // }); - //} + public Command lowerElevator() { + // Placeholder + return runOnce( + () -> { + /* one-time action goes here */ + setMotorElevatorSpeed(-0.1); + }); + } - // public Command lowerElevator() { - // // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(-0.1); - // }); - // } + public void debuggingMethod() { + SmartDashboard.putNumber("Elevator Encoder", m_Encoder1.get()); + SmartDashboard.putBoolean("Elevator Top Limit Switch", m_elevatorTopLimitSwitch.get()); + SmartDashboard.putBoolean("Elevator Bottom Limit Switch", m_elevatorBottomLimitSwitch.get()); + } public boolean isElevatorAtTop() { // Placeholder - if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { - return false; - } - else if(m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()){ - System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + if (m_Encoder1.get() >= 300 && m_elevatorTopLimitSwitch.get()) { // Placeholder. replace 0 in .get methods with the value that encoder would be at if at the top return true; } - else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { - System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + else if(m_Encoder1.get() >= 300 || m_elevatorTopLimitSwitch.get()){ + System.out.println("Error: Encoder And TOP Limit Switch Mismatched"); return true; } else { @@ -90,15 +87,11 @@ else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_el public boolean isElevatorAtBottom() { // Placeholder - if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { - return false; - } - else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && m_elevatorBottomLimitSwitch.get()){ - System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + if (m_Encoder1.get() <= 0 && m_elevatorBottomLimitSwitch.get()) { // Placeholder. replace 0s in .get methods with what encoder would be at when at bottom return true; } - else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { - System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + else if (m_Encoder1.get() <= 0 || m_elevatorBottomLimitSwitch.get()){ + System.out.println("Error: Encoder And BOTTOM Limit Switch Mismatched"); return true; } else { @@ -112,11 +105,12 @@ public void setMotorElevatorSpeed(double voltage) { // BEN: If this method truly will be run once (as above), then the stop conditions will only be evaluated once. // We need to check the conditions continually somehow. - if (voltage > 0 && isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up + debuggingMethod(); + if (voltage > 0 && !isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving up"); - } else if (voltage < 0 && isElevatorAtBottom()) {//or if its at bottom make move down + } else if (voltage < 0 && !isElevatorAtBottom()) {//or if its at bottom make move down m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving down"); @@ -128,19 +122,19 @@ public void setMotorElevatorSpeed(double voltage) { } - public void moveElevatorToPreset(ElevatorLevels desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements + public void moveElevatorToPreset(ElevatorPreset desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements switch (desiredPreset) { case Level1: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue); break; case Level2: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue); break; case Level3: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue); break; case Level4: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue); break; } } @@ -150,47 +144,41 @@ public void moveElevatorToPreset(ElevatorLevels desiredPreset) {//tells elevator * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a selective factorial. Yeah idk why I said that. Moving on * @param position */ - public void moveElevatorToPosition (double position, ElevatorLevels desiredPreset) {//moves elevator to a certain position based on the encoder value + public void moveElevatorToPosition (double position) { // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! - if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom - m_elevatorLeftMotor.set(-ElevatorConstants.kMotorElevatorSpeed); - m_elevatorRightMotor.set(ElevatorConstants.kMotorElevatorSpeed); + debuggingMethod(); + if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom + m_elevatorLeftMotor.set(-motorElevatorSpeed); + m_elevatorRightMotor.set(motorElevatorSpeed); Utility.printLn("Elevator is moving down"); - } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top - m_elevatorLeftMotor.set(ElevatorConstants.kMotorElevatorSpeed); - m_elevatorRightMotor.set(-ElevatorConstants.kMotorElevatorSpeed); + } else if (m_Encoder1.get() >= (position -5) && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top + m_elevatorLeftMotor.set(motorElevatorSpeed); + m_elevatorRightMotor.set(-motorElevatorSpeed); Utility.printLn("Elevator is moving up"); } else { m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); Utility.printLn("Elevator is at the top, bottom, desired position, or error occurred and therefore the motors have been stopped"); - currentElevatorState = desiredPreset; } } - public ElevatorLevels getElevatorState() { - return currentElevatorState; - } - - public BooleanSupplier isElevatorAtDesiredState(ElevatorLevels desiredState) { - return () -> currentElevatorState == desiredState; - } + - public Command commandVoltage(double leftTrigger, double rightTrigger) {//just exists because just felt like it + public Command commandVoltage(double voltage) {//just exists because just felt like it return runOnce( () -> { - setMotorElevatorSpeed(rightTrigger - leftTrigger); + setMotorElevatorSpeed(voltage); } ); } - public Command commandMoveToPreset(ElevatorLevels desiredPreset) {//The first function in a very tall dependancy tree - return runOnce( - () -> { - moveElevatorToPreset(desiredPreset); - } - ); - } + // public Command commandMoveToPreset(ElevatorPreset desiredPreset) {//does the function at line 134 but with more... you know... + // return runOnce( + // () -> { + // moveElevatorToPreset(desiredPreset); + // } + // ); + // } } //a comment because why not :) From 936053d78389693111a9c6d8c91f559c37271a5d Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 15 Feb 2025 12:42:20 -0600 Subject: [PATCH 07/32] Last merge didn't work (trying to open merge page between files) - H --- .../main/java/frc/robot/RobotContainer.java | 291 +++++++++++++----- .../robot/subsystems/ElevatorSubsystem.java | 140 +++++---- 2 files changed, 294 insertions(+), 137 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 68d2ae7..b73be27 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -2,100 +2,245 @@ // 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. +/* + * This Is The Input List. Whenever Adding A New Input, Make Sure To Add It To The List Below + * + * Driver Controller: + * A - Brake + * B - Point + * Back + Y - SysId Dynamic Forward + * Back + X - SysId Dynamic Reverse + * Start + Y - SysId Quasistatic Forward + * Start + X - SysId Quasistatic Reverse + * DPad Up - Coral Arm Up + * DPad Down - Coral Arm Down + * DPad Center - Coral Arm Stop + * Right Stick - Coral Arm Emergency Stop + * LB - Reset Field Centric Seed + * + * Auxiliary Controller: + * A - Level 1 Elevator + Coral Arm + * B - Level 2 Elevator + Coral Arm + * Y - Level 3 Elevator + Coral Arm + * X - Level 4 Elevator + Coral Arm + * Thumbpad Down - Algae Bar Intake + * Thumbpad Up - Algae Bar Spit Out + * Thumbpad left - Algae Bar Spin Slow + * LB - Elevator Level 1 + * RB - Elevator Level 2 + * LT - Elevator Level 3 + */ + package frc.robot; +import static edu.wpi.first.units.Units.*; + +import com.ctre.phoenix6.swerve.SwerveModule.DriveRequestType; +import com.pathplanner.lib.auto.AutoBuilder; +import com.pathplanner.lib.auto.NamedCommands; +import com.pathplanner.lib.commands.PathPlannerAuto; +import com.ctre.phoenix6.swerve.SwerveRequest; +import java.lang.Math; +import frc.robot.Constants.OperatorConstants; +import frc.robot.generated.TunerConstants; +import frc.robot.commands.ExampleCommand; +import frc.robot.subsystems.ElevatorSubsystem; +import frc.robot.subsystems.ExampleSubsystem; +import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; +import frc.robot.subsystems.ElevatorSubsystem.ElevatorLevels; +import frc.robot.subsystems.AlgaeArmSubsystem; +import frc.robot.subsystems.CommandSwerveDrivetrain; +import frc.robot.subsystems.CoralArmSubsystem; import edu.wpi.first.wpilibj2.command.Command; -import edu.wpi.first.wpilibj2.command.Commands; -import edu.wpi.first.wpilibj2.command.FunctionalCommand; -import edu.wpi.first.wpilibj2.command.RunCommand; -import edu.wpi.first.wpilibj2.command.Subsystem; -// import edu.wpi.first.wpilibj2.command.StartEndCommand; import edu.wpi.first.wpilibj2.command.button.CommandXboxController; -// import frc.robot.subsystems.TestSubsystem; -// import frc.robot.subsystems.ElevatorSubsystem; -import frc.robot.subsystems.ElevatorSubsystem; -import frc.robot.subsystems.ElevatorSubsystem.ElevatorPreset; - -// import edu.wpi.first.wpilibj2. +import edu.wpi.first.wpilibj2.command.button.Trigger; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; +/** little secret comment OwO + * 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 { - // Controllers - CommandXboxController driveController = new CommandXboxController(0); - CommandXboxController auxController = new CommandXboxController(1); - - //Subsystems - // TestSubsystem testSubsystem = new TestSubsystem(); - ElevatorSubsystem elevatorSubsystem = new ElevatorSubsystem(10, 11); - - // Variables - int count = 0; + // The robot's subsystems and commands are defined here... + // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); + private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( + Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem(Constants.AlgaeArmConstants.algaeArmBarID); + private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); + private final CommandXboxController m_driverController = + new CommandXboxController(OperatorConstants.kDriverControllerPort); - // constructor - public RobotContainer() { - configureBindings(); - } - - private void configureBindings() { - - // auxController.leftTrigger().whileTrue(elevatorSubsystem.commandVoltage(0.1)).onFalse(elevatorSubsystem.commandVoltage(0.0)); - // auxController.rightTrigger().whileTrue(elevatorSubsystem.commandVoltage(-0.1)).onFalse(elevatorSubsystem.commandVoltage(0.0)); - - auxController.leftTrigger().onTrue(new RunCommand(() -> elevatorSubsystem.setMotorElevatorSpeed(0.1) , elevatorSubsystem)); - auxController.rightTrigger().onTrue(new RunCommand(() -> elevatorSubsystem.setMotorElevatorSpeed(-0.1) , elevatorSubsystem)); - - auxController.a().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level1), elevatorSubsystem)); - auxController.b().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level2), elevatorSubsystem)); - auxController.x().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level3), elevatorSubsystem)); - auxController.y().onTrue(new RunCommand(() -> elevatorSubsystem.moveElevatorToPreset(ElevatorPreset.Level4), elevatorSubsystem)); - - - - - - - - - - - - - - - - + private final SendableChooser autoChooser; + /* Swerve drive platform setup */ // From Swerve Project Generator + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max angular velocity + + /* Setting up bindings for necessary control of the swerve drive platform */ + private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric() + .withDeadband(MaxSpeed * 0.1).withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband + .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors + private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); + private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); + + private final Telemetry logger = new Telemetry(MaxSpeed); + private final CommandXboxController m_auxillaryController = new CommandXboxController(Constants.OperatorConstants.kAuxiliaryControllerPort); + public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + // End of Swerve Drive Platform setup + /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public RobotContainer() { + - // Test Commands + // Build an auto chooser. This will use Commands.none() as the default option. + autoChooser = AutoBuilder.buildAutoChooser(); - /* - controller.a().onTrue(testSubsystem.testCommand()); + // Another option that allows you to specify the default auto by its name + // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); - controller.b().onTrue(new RunCommand(() -> testSubsystem.testMethod1(), testSubsystem)); + SmartDashboard.putData("Auto Chooser", autoChooser); - controller.x().onTrue(Commands.startEnd( - () -> testSubsystem.testMethod2(), - () -> testSubsystem.testMethod3(), - testSubsystem)); + // Register named commands for use in autonomous routines + // NamedCommands.registerCommand("Example Command", m_exampleSubsystem.exampleMethodCommand()); + // NamedCommands.registerCommand("MoveElevatorToPresets", m_elevatorSubsystem.commandMoveToPreset(null)); + + // Configure the trigger bindings + configureBindings(Math.random()); + } - controller.y().onTrue( - new FunctionalCommand( - () -> testSubsystem.testMethod0(), // prints something once it runs - () -> count++, //increments count variable - interrupted -> testSubsystem.testMethod3(), - () -> count >= 10, - testSubsystem) + /** + * 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}. + * @param random TODO + */ + private void configureBindings(double random) { + + // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is pressed, + // cancelling on release. + // m_auxiliaryController.axisLessThan(2, .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about holding it in place? + // m_auxiliaryController.axisGreaterThan(3, .4).whileTrue(m_elevatorSubsystem.raiseElevator()); + + + // Elevator Subsystem + //The commands below make the elevator go to certain levels + m_auxillaryController.leftBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left bumper pressed, move to level 1. Else, go to top + m_auxillaryController.rightBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If right bumper pressed, move to level 2. Else, go to top + m_auxillaryController.leftTrigger().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left trigger pressed, move to level 3. Else, go to top + + // Algae Arm Subsystem + m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop + m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop + m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop + //m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); + //This command was taken out due to clashing and we already have a command that does the same thing + + // Command Compositions for Elevator + Coral + m_auxillaryController.a().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.b().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.y().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) + ); + m_auxillaryController.x().onTrue( + Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) + ) ); - */ + // Coral Arm Subsystem + m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));//Makes Coral Arm Go Up + m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));//Makes Coral Arm Go Down + m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));//Stops Coral Arm + m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());//Emergency Stop for Coral Arm(in case it goes past the top or bottom) Note: I don't know if the onTrue method will only run once, so test it before you use it + + + // Drive Subsystem + // Code below is from swerve drive project generator + + // Note that X is defined as forward according to WPILib convention, + // and Y is defined as to the left according to WPILib convention. + drivetrain.setDefaultCommand( + // Drivetrain will execute this command periodically + drivetrain.applyRequest(() -> + drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward with negative Y (forward) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with negative X (left) + ) + ); + m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); + m_driverController.b().whileTrue(drivetrain.applyRequest(() -> + point.withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())) + )); + + // Run SysId routines when holding back/start and X/Y. + // Note that each routine should be run exactly once in a single log. + m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + + // reset the field-centric heading on left bumper press + m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + + // drivetrain.addVisionMeasurement(LimelightHelpers.getBotPose2d("DriveCamera"), random); + + // drivetrain.getModule(0). + + drivetrain.registerTelemetry(logger::telemeterize); + + } + /** + * Use this to pass the autonomous command to the main {@link Robot} class. + * + * @return the command to run in autonomous + */ public Command getAutonomousCommand() { - return Commands.print("No autonomous command configured"); - - + // An example command will be run in autonomous + // return Autos.exampleAuto(m_exampleSubsystem); + return autoChooser.getSelected(); } } +//another why not comment :) +// I do love some good comments :D \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 785341f..785a867 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -1,11 +1,11 @@ package frc.robot.subsystems; - import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.Command; //import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.Encoder; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; + +import java.util.function.BooleanSupplier; //import com.ctre.phoenix.Util; import com.ctre.phoenix6.hardware.TalonFX; @@ -24,60 +24,63 @@ public class ElevatorSubsystem extends SubsystemBase {//makes elevator subsystem private final Encoder m_Encoder1; // private TitanQuadEncoder m_elevatorEncoder; private double m_elevatorEncoder; // Placeholder - // Constants - private double motorElevatorSpeed; + + private ElevatorLevels currentElevatorState; + public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {//this is da place where stuff is actually initialized m_elevatorEncoder = 0; // Placeholder - motorElevatorSpeed = ElevatorConstants.kMotorElevatorSpeed; - m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); - m_elevatorTopLimitSwitch = new DigitalInput(7); - m_elevatorBottomLimitSwitch = new DigitalInput(8); + m_elevatorTopLimitSwitch = new DigitalInput(1); + m_elevatorBottomLimitSwitch = new DigitalInput(2); m_Encoder1 = new Encoder(0, 1); + currentElevatorState = ElevatorLevels.Lowest; } -public enum ElevatorPreset { - Level1, +public enum ElevatorLevels { + Lowest, // bottom + Level1, // Levels of the reef Level2, Level3, - Level4 + Level4, + Highest // top } - public Command raiseElevator() { - // Placeholder - return runOnce( - () -> { - /* one-time action goes here */ - setMotorElevatorSpeed(0.1); - }); - } - public Command lowerElevator() { - // Placeholder - return runOnce( - () -> { - /* one-time action goes here */ - setMotorElevatorSpeed(-0.1); - }); - } +//The commands below that are commented are currently not being used and have been replaced. They are kept just cause - public void debuggingMethod() { - SmartDashboard.putNumber("Elevator Encoder", m_Encoder1.get()); - SmartDashboard.putBoolean("Elevator Top Limit Switch", m_elevatorTopLimitSwitch.get()); - SmartDashboard.putBoolean("Elevator Bottom Limit Switch", m_elevatorBottomLimitSwitch.get()); - } + //public Command raiseElevator() { + // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(0.1); + // }); + //} + + // public Command lowerElevator() { + // // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(-0.1); + // }); + // } public boolean isElevatorAtTop() { // Placeholder - if (m_Encoder1.get() >= 300 && m_elevatorTopLimitSwitch.get()) { // Placeholder. replace 0 in .get methods with the value that encoder would be at if at the top + if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + return false; + } + else if(m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()){ + System.out.println("Error: Encoder And Top Limit Switch Mismatched"); return true; } - else if(m_Encoder1.get() >= 300 || m_elevatorTopLimitSwitch.get()){ - System.out.println("Error: Encoder And TOP Limit Switch Mismatched"); + else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + System.out.println("Error: Encoder And Top Limit Switch Mismatched"); return true; } else { @@ -87,11 +90,15 @@ else if(m_Encoder1.get() >= 300 || m_elevatorTopLimitSwitch.get()){ public boolean isElevatorAtBottom() { // Placeholder - if (m_Encoder1.get() <= 0 && m_elevatorBottomLimitSwitch.get()) { // Placeholder. replace 0s in .get methods with what encoder would be at when at bottom + if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + return false; + } + else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && m_elevatorBottomLimitSwitch.get()){ + System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); return true; } - else if (m_Encoder1.get() <= 0 || m_elevatorBottomLimitSwitch.get()){ - System.out.println("Error: Encoder And BOTTOM Limit Switch Mismatched"); + else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); return true; } else { @@ -105,12 +112,11 @@ public void setMotorElevatorSpeed(double voltage) { // BEN: If this method truly will be run once (as above), then the stop conditions will only be evaluated once. // We need to check the conditions continually somehow. - debuggingMethod(); - if (voltage > 0 && !isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up + if (voltage > 0 && isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving up"); - } else if (voltage < 0 && !isElevatorAtBottom()) {//or if its at bottom make move down + } else if (voltage < 0 && isElevatorAtBottom()) {//or if its at bottom make move down m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving down"); @@ -122,19 +128,19 @@ public void setMotorElevatorSpeed(double voltage) { } - public void moveElevatorToPreset(ElevatorPreset desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements + public void moveElevatorToPreset(ElevatorLevels desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements switch (desiredPreset) { case Level1: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); break; case Level2: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); break; case Level3: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); break; case Level4: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue); + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); break; } } @@ -144,41 +150,47 @@ public void moveElevatorToPreset(ElevatorPreset desiredPreset) {//tells elevator * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a selective factorial. Yeah idk why I said that. Moving on * @param position */ - public void moveElevatorToPosition (double position) { + public void moveElevatorToPosition (double position, ElevatorLevels desiredPreset) {//moves elevator to a certain position based on the encoder value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! - debuggingMethod(); - if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom - m_elevatorLeftMotor.set(-motorElevatorSpeed); - m_elevatorRightMotor.set(motorElevatorSpeed); + if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom + m_elevatorLeftMotor.set(-ElevatorConstants.kMotorElevatorSpeed); + m_elevatorRightMotor.set(ElevatorConstants.kMotorElevatorSpeed); Utility.printLn("Elevator is moving down"); - } else if (m_Encoder1.get() >= (position -5) && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top - m_elevatorLeftMotor.set(motorElevatorSpeed); - m_elevatorRightMotor.set(-motorElevatorSpeed); + } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top + m_elevatorLeftMotor.set(ElevatorConstants.kMotorElevatorSpeed); + m_elevatorRightMotor.set(-ElevatorConstants.kMotorElevatorSpeed); Utility.printLn("Elevator is moving up"); } else { m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); Utility.printLn("Elevator is at the top, bottom, desired position, or error occurred and therefore the motors have been stopped"); + currentElevatorState = desiredPreset; } } - + public ElevatorLevels getElevatorState() { + return currentElevatorState; + } + + public BooleanSupplier isElevatorAtDesiredState(ElevatorLevels desiredState) { + return () -> currentElevatorState == desiredState; + } - public Command commandVoltage(double voltage) {//just exists because just felt like it + public Command commandVoltage(double leftTrigger, double rightTrigger) {//just exists because just felt like it return runOnce( () -> { - setMotorElevatorSpeed(voltage); + setMotorElevatorSpeed(rightTrigger - leftTrigger); } ); } - // public Command commandMoveToPreset(ElevatorPreset desiredPreset) {//does the function at line 134 but with more... you know... - // return runOnce( - // () -> { - // moveElevatorToPreset(desiredPreset); - // } - // ); - // } + public Command commandMoveToPreset(ElevatorLevels desiredPreset) {//The first function in a very tall dependancy tree + return runOnce( + () -> { + moveElevatorToPreset(desiredPreset); + } + ); + } } //a comment because why not :) From 9a1122f66444c5ac8b4903dd40ff6a2194126d48 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 15 Feb 2025 14:18:08 -0600 Subject: [PATCH 08/32] Added Formatter --- 2024-2025/main-bot/src/main/java/frc/.clang-format | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 2024-2025/main-bot/src/main/java/frc/.clang-format diff --git a/2024-2025/main-bot/src/main/java/frc/.clang-format b/2024-2025/main-bot/src/main/java/frc/.clang-format new file mode 100644 index 0000000..bdb418e --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/.clang-format @@ -0,0 +1,6 @@ +Language: Java +BasedOnStyle: Microsoft + +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveDeclarations: Consecutive +ColumnLimit: 0 \ No newline at end of file From e912bff297c9568e38be3273126ded69df8651b8 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 15 Feb 2025 14:20:06 -0600 Subject: [PATCH 09/32] Added Limelight Functionality - H --- .../main/java/frc/robot/LimelightHelpers.java | 1647 +++++++++++++++++ .../main/java/frc/robot/RobotContainer.java | 298 +-- .../robot/subsystems/LimeLightSubsystem.java | 143 ++ 3 files changed, 1966 insertions(+), 122 deletions(-) create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/LimelightHelpers.java create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java diff --git a/2024-2025/main-bot/src/main/java/frc/robot/LimelightHelpers.java b/2024-2025/main-bot/src/main/java/frc/robot/LimelightHelpers.java new file mode 100644 index 0000000..ddafedd --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/LimelightHelpers.java @@ -0,0 +1,1647 @@ +//LimelightHelpers v1.11 (REQUIRES LLOS 2025.0 OR LATER) + +package frc.robot; + +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; +import frc.robot.LimelightHelpers.LimelightResults; +import frc.robot.LimelightHelpers.PoseEstimate; +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.Translation3d; +import edu.wpi.first.math.util.Units; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation2d; + +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 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 java.util.concurrent.ConcurrentHashMap; + +/** + * 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/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index b73be27..2216e37 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -23,12 +23,14 @@ * B - Level 2 Elevator + Coral Arm * Y - Level 3 Elevator + Coral Arm * X - Level 4 Elevator + Coral Arm - * Thumbpad Down - Algae Bar Intake - * Thumbpad Up - Algae Bar Spit Out - * Thumbpad left - Algae Bar Spin Slow - * LB - Elevator Level 1 - * RB - Elevator Level 2 - * LT - Elevator Level 3 + * Left Joystick up - Move Elevator up + * Left Joystick down - Move Elevator down + * LB - Coral Arm Up + * RB - Coral Arm Down + * DPad Center - Coral Arm Stop + * LT - Algae intake + * RT - Algae outtake + * */ package frc.robot; @@ -60,11 +62,11 @@ import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; - -/** little secret comment OwO - * 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 +/** + * little secret comment OwO 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 { @@ -72,34 +74,38 @@ public class RobotContainer { // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); - private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem(Constants.AlgaeArmConstants.algaeArmBarID); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( + Constants.AlgaeArmConstants.algaeArmBarID); private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); - private final CommandXboxController m_driverController = - new CommandXboxController(OperatorConstants.kDriverControllerPort); - + private final CommandXboxController m_driverController = new CommandXboxController( + OperatorConstants.kDriverControllerPort); + private final SendableChooser autoChooser; /* Swerve drive platform setup */ // From Swerve Project Generator - private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed - private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max angular velocity - - /* Setting up bindings for necessary control of the swerve drive platform */ - private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric() - .withDeadband(MaxSpeed * 0.1).withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband - .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors - private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); - private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); - - private final Telemetry logger = new Telemetry(MaxSpeed); + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max + // angular velocity - private final CommandXboxController m_auxillaryController = new CommandXboxController(Constants.OperatorConstants.kAuxiliaryControllerPort); + /* Setting up bindings for necessary control of the swerve drive platform */ + private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) + .withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband + .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors + private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); + private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); - public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + private final Telemetry logger = new Telemetry(MaxSpeed); - // End of Swerve Drive Platform setup + private final CommandXboxController m_auxillaryController = new CommandXboxController( + Constants.OperatorConstants.kAuxiliaryControllerPort); - /** The container for the robot. Contains subsystems, OI devices, and commands. */ + public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + + // End of Swerve Drive Platform setup + + /** + * The container for the robot. Contains subsystems, OI devices, and commands. + */ public RobotContainer() { - // Build an auto chooser. This will use Commands.none() as the default option. autoChooser = AutoBuilder.buildAutoChooser(); @@ -110,125 +116,173 @@ public RobotContainer() { SmartDashboard.putData("Auto Chooser", autoChooser); // Register named commands for use in autonomous routines - // NamedCommands.registerCommand("Example Command", m_exampleSubsystem.exampleMethodCommand()); - // NamedCommands.registerCommand("MoveElevatorToPresets", m_elevatorSubsystem.commandMoveToPreset(null)); - + // NamedCommands.registerCommand("Example Command", + // m_exampleSubsystem.exampleMethodCommand()); + // NamedCommands.registerCommand("MoveElevatorToPresets", + // m_elevatorSubsystem.commandMoveToPreset(null)); + // Configure the trigger bindings configureBindings(Math.random()); } /** - * 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}. - * @param random TODO + * 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}. + * + * @param random TODO */ private void configureBindings(double random) { - // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is pressed, + // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is + // pressed, // cancelling on release. - // m_auxiliaryController.axisLessThan(2, .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about holding it in place? - // m_auxiliaryController.axisGreaterThan(3, .4).whileTrue(m_elevatorSubsystem.raiseElevator()); + // m_auxiliaryController.axisLessThan(2, + // .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about + // holding it in place? + // m_auxiliaryController.axisGreaterThan(3, + // .4).whileTrue(m_elevatorSubsystem.raiseElevator()); - - // Elevator Subsystem - //The commands below make the elevator go to certain levels - m_auxillaryController.leftBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left bumper pressed, move to level 1. Else, go to top - m_auxillaryController.rightBumper().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If right bumper pressed, move to level 2. Else, go to top - m_auxillaryController.leftTrigger().onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)).onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));//If left trigger pressed, move to level 3. Else, go to top + // Elevator Subsystem + // The commands below make the elevator go to certain levels + m_auxillaryController.leftBumper() + .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)) + .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If left bumper + // pressed, move to + // level 1. Else, go + // to top + m_auxillaryController.rightBumper() + .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)) + .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If right bumper + // pressed, move to + // level 2. Else, go + // to top + m_auxillaryController.leftTrigger() + .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)) + .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If left trigger + // pressed, move to + // level 3. Else, go + // to top // Algae Arm Subsystem - m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algaeBar spin faster if A pressed, else stop - m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spit out ball if B pressed, else stop - m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)).onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));//makes algae bar spin slowly if Y pressed, else stop - //m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) - // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); - //This command was taken out due to clashing and we already have a command that does the same thing + m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar spin faster if A + // pressed, else stop + m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spit out ball if B + // pressed, else stop + m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spin slowly if Y + // pressed, else stop + // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); + // This command was taken out due to clashing and we already have a command that + // does the same thing // Command Compositions for Elevator + Coral - m_auxillaryController.a().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Level 1 Preset + m_auxillaryController.a().onTrue(Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.b().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); + // Level 2 Preset + m_auxillaryController.b().onTrue(Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.y().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); + // Level 3 preset + m_auxillaryController.y().onTrue(Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); - m_auxillaryController.x().onTrue( - Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); + // Level 4 preset + m_auxillaryController.x().onTrue(Commands.sequence( + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), + m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down).until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest).until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)) - ) - ); + m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) + .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); // Coral Arm Subsystem - m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));//Makes Coral Arm Go Up - m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));//Makes Coral Arm Go Down - m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));//Stops Coral Arm - m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());//Emergency Stop for Coral Arm(in case it goes past the top or bottom) Note: I don't know if the onTrue method will only run once, so test it before you use it - + m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));// Makes + // Coral + // Arm + // Go Up + m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));// Makes + // Coral + // Arm + // Go + // Down + m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));// Stops + // Coral + // Arm + m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());// Emergency Stop for Coral Arm(in case + // it goes past the top or bottom) Note: + // I don't know if the onTrue method + // will only run once, so test it before + // you use it // Drive Subsystem // Code below is from swerve drive project generator - // Note that X is defined as forward according to WPILib convention, - // and Y is defined as to the left according to WPILib convention. - drivetrain.setDefaultCommand( - // Drivetrain will execute this command periodically - drivetrain.applyRequest(() -> - drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward with negative Y (forward) - .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) - .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with negative X (left) - ) - ); - m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); - m_driverController.b().whileTrue(drivetrain.applyRequest(() -> - point.withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())) + // Note that X is defined as forward according to WPILib convention, + // and Y is defined as to the left according to WPILib convention. + drivetrain.setDefaultCommand( + // Drivetrain will execute this command periodically + drivetrain.applyRequest(() -> drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward + // with negative Y + // (forward) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with + // negative X (left) )); - - // Run SysId routines when holding back/start and X/Y. - // Note that each routine should be run exactly once in a single log. - m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); - m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); - m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); - m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); - - // reset the field-centric heading on left bumper press - m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); + m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point + .withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); + + // Run SysId routines when holding back/start and X/Y. + // Note that each routine should be run exactly once in a single log. + m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + + // reset the field-centric heading on left bumper press + m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); - // drivetrain.addVisionMeasurement(LimelightHelpers.getBotPose2d("DriveCamera"), random); + drivetrain.addVisionMeasurement(LimelightHelpers.getBotPose2d("DriveCamera"), LimelightHelpers.get); - // drivetrain.getModule(0). + // drivetrain.getModule(0). - drivetrain.registerTelemetry(logger::telemeterize); + drivetrain.registerTelemetry(logger::telemeterize); - } /** @@ -242,5 +296,5 @@ public Command getAutonomousCommand() { return autoChooser.getSelected(); } } -//another why not comment :) +// another why not comment :) // I do love some good comments :D \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java new file mode 100644 index 0000000..f891237 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java @@ -0,0 +1,143 @@ +package frc.robot.subsystems; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.estimator.PoseEstimator; +import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.kinematics.SwerveModulePosition; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.LimelightHelpers; +// import edu.first.LimelightHelpers; +// import edu.first.wpilibj. + +// bunch of random imports + + +import edu.wpi.first.math.kinematics.SwerveDriveKinematics; + +import edu.wpi.first.math.util.Units; +import edu.wpi.first.wpilibj.AnalogGyro; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.DriverStation.Alliance; + + +public class LimeLightSubsystem extends SubsystemBase { + + int FLIndex = 0; + int FRIndex = 1; + int BLIndex = 2; + int BRIndex = 3; + + private final SwerveDrivePoseEstimator m_poseEstimator; + + + public LimeLightSubsystem(CommandSwerveDrivetrain swerveDriveTrain) { + + m_poseEstimator = + new SwerveDrivePoseEstimator( + swerveDriveTrain.getKinematics(), + swerveDriveTrain.getPigeon2().getRotation2d(), + new SwerveModulePosition[] { + swerveDriveTrain.getModule(0).getPosition(true), + swerveDriveTrain.getModule(1).getPosition(true), + swerveDriveTrain.getModule(2).getPosition(true), + swerveDriveTrain.getModule(3).getPosition(true) + }, + new Pose2d(), + VecBuilder.fill(0.05, 0.05, Units.degreesToRadians(5)), + VecBuilder.fill(0.5, 0.5, Units.degreesToRadians(30))); + } + + + + public void updateOdometry(CommandSwerveDrivetrain swerveDriveTrain) { + + m_poseEstimator.update( + swerveDriveTrain.getPigeon2().getRotation2d(), + new SwerveModulePosition[] { + swerveDriveTrain.getModule(0).getPosition(true), + swerveDriveTrain.getModule(1).getPosition(true), + swerveDriveTrain.getModule(2).getPosition(true), + swerveDriveTrain.getModule(3).getPosition(true) + }); + + + // Is this boolean ever changing at runtime? If not, it should be a final variable + boolean useMegaTag2 = true; //set to false to use MegaTag1 + boolean doRejectUpdate = false; + if(useMegaTag2 == false) + { + // Use this line to know whether we are part of an alliance (we might not be if we are not in a match presently) + // DriverStation.getAlliance().isEmpty() + + // Use this line to know whether we are on the blue alliance + // DriverStation.getAlliance().get().equals(Alliance.Blue); + + // This should change depending on the alliance we are on + LimelightHelpers.PoseEstimate mt1 = LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight"); + + if(mt1.tagCount == 1 && mt1.rawFiducials.length == 1) + { + if(mt1.rawFiducials[0].ambiguity > .7) + { + doRejectUpdate = true; + } + if(mt1.rawFiducials[0].distToCamera > 3) + { + doRejectUpdate = true; + } + } + if(mt1.tagCount == 0) + { + doRejectUpdate = true; + } + + if(!doRejectUpdate) + { + m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.5,.5,9999999)); + m_poseEstimator.addVisionMeasurement( + mt1.pose, + mt1.timestampSeconds); + + swerveDriveTrain.addVisionMeasurement( + mt1.pose, + mt1.timestampSeconds, + VecBuilder.fill(.5,.5,9999999)); + } + } + else if (useMegaTag2 == true) + { + LimelightHelpers.SetRobotOrientation("limelight", m_poseEstimator.getEstimatedPosition().getRotation().getDegrees(), 0, 0, 0, 0, 0); + + // Same thing as above + LimelightHelpers.PoseEstimate mt2 = LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2("limelight"); + // NOTE! The .getAngularVelocityZWorld() update is different from orignal code. The method .getRate() is depreicated and will be removed soom, so we have switched to this method. However, it is CCW+ instead of CW+, and may not return the same value + if(Math.abs(swerveDriveTrain.getPigeon2().getAngularVelocityZWorld().getValueAsDouble()) > 720) // if our angular velocity is greater than 720 degrees per second, ignore vision updates + { + doRejectUpdate = true; + } + if(mt2.tagCount == 0) + { + doRejectUpdate = true; + } + if(!doRejectUpdate) + { + m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.7,.7,9999999)); + m_poseEstimator.addVisionMeasurement( + mt2.pose, + mt2.timestampSeconds); + + swerveDriveTrain.addVisionMeasurement( + mt2.pose, + mt2.timestampSeconds, + VecBuilder.fill(.7,.7,9999999)); + } + } + } + + public PoseEstimator getPoseEstimator() { + return m_poseEstimator; + } +} From d012959edb5ff19bce3f29adb5d02cdc26d2b810 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 15 Feb 2025 14:24:54 -0600 Subject: [PATCH 10/32] Pathplanner update - H --- ...nnerLib-2025.2.1.json => PathplannerLib-2025.2.3.json} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename 2024-2025/main-bot/vendordeps/{PathplannerLib-2025.2.1.json => PathplannerLib-2025.2.3.json} (87%) diff --git a/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json b/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.3.json similarity index 87% rename from 2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json rename to 2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.3.json index 71e25f3..9151ce4 100644 --- a/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.1.json +++ b/2024-2025/main-bot/vendordeps/PathplannerLib-2025.2.3.json @@ -1,7 +1,7 @@ { - "fileName": "PathplannerLib-2025.2.1.json", + "fileName": "PathplannerLib-2025.2.3.json", "name": "PathplannerLib", - "version": "2025.2.1", + "version": "2025.2.3", "uuid": "1b42324f-17c6-4875-8e77-1c312bc8c786", "frcYear": "2025", "mavenUrls": [ @@ -12,7 +12,7 @@ { "groupId": "com.pathplanner.lib", "artifactId": "PathplannerLib-java", - "version": "2025.2.1" + "version": "2025.2.3" } ], "jniDependencies": [], @@ -20,7 +20,7 @@ { "groupId": "com.pathplanner.lib", "artifactId": "PathplannerLib-cpp", - "version": "2025.2.1", + "version": "2025.2.3", "libName": "PathplannerLib", "headerClassifier": "headers", "sharedLibrary": false, From 0c7460b79788dc684f744ebcd431342cf9b7ac4b Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 12:21:46 -0600 Subject: [PATCH 11/32] Adjusted elevator commands and the buttons for each subsystem to fit Vy's desired buttons --- .../main/java/frc/robot/RobotContainer.java | 451 +++++++++--------- .../robot/subsystems/ElevatorSubsystem.java | 190 ++++---- 2 files changed, 319 insertions(+), 322 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 2216e37..0f340f5 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -49,7 +49,7 @@ import frc.robot.subsystems.ElevatorSubsystem; import frc.robot.subsystems.ExampleSubsystem; import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; -import frc.robot.subsystems.ElevatorSubsystem.ElevatorLevels; +import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; import frc.robot.subsystems.AlgaeArmSubsystem; import frc.robot.subsystems.CommandSwerveDrivetrain; import frc.robot.subsystems.CoralArmSubsystem; @@ -60,7 +60,9 @@ import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; +import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; /** * little secret comment OwO This class is where the bulk of the robot should be @@ -70,231 +72,226 @@ * 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(); - private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( - Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); - private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( - Constants.AlgaeArmConstants.algaeArmBarID); - private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); - private final CommandXboxController m_driverController = new CommandXboxController( - OperatorConstants.kDriverControllerPort); - - private final SendableChooser autoChooser; - /* Swerve drive platform setup */ // From Swerve Project Generator - private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed - private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second max - // angular velocity - - /* Setting up bindings for necessary control of the swerve drive platform */ - private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) - .withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband - .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors - private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); - private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); - - private final Telemetry logger = new Telemetry(MaxSpeed); - - private final CommandXboxController m_auxillaryController = new CommandXboxController( - Constants.OperatorConstants.kAuxiliaryControllerPort); - - public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); - - // End of Swerve Drive Platform setup - - /** - * The container for the robot. Contains subsystems, OI devices, and commands. - */ - public RobotContainer() { - - // Build an auto chooser. This will use Commands.none() as the default option. - autoChooser = AutoBuilder.buildAutoChooser(); - - // Another option that allows you to specify the default auto by its name - // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); - - SmartDashboard.putData("Auto Chooser", autoChooser); - - // Register named commands for use in autonomous routines - // NamedCommands.registerCommand("Example Command", - // m_exampleSubsystem.exampleMethodCommand()); - // NamedCommands.registerCommand("MoveElevatorToPresets", - // m_elevatorSubsystem.commandMoveToPreset(null)); - - // Configure the trigger bindings - configureBindings(Math.random()); - } - - /** - * 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}. - * - * @param random TODO - */ - private void configureBindings(double random) { - - // Schedule `raiseElevator` when the Auxiliary Controller's left trigger is - // pressed, - // cancelling on release. - // m_auxiliaryController.axisLessThan(2, - // .4).whileTrue(m_elevatorSubsystem.lowerElevator()); // BEN: What about - // holding it in place? - // m_auxiliaryController.axisGreaterThan(3, - // .4).whileTrue(m_elevatorSubsystem.raiseElevator()); - - // Elevator Subsystem - // The commands below make the elevator go to certain levels - m_auxillaryController.leftBumper() - .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level1)) - .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If left bumper - // pressed, move to - // level 1. Else, go - // to top - m_auxillaryController.rightBumper() - .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level2)) - .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If right bumper - // pressed, move to - // level 2. Else, go - // to top - m_auxillaryController.leftTrigger() - .onTrue(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level3)) - .onFalse(m_elevatorSubsystem.commandMoveToPreset(ElevatorSubsystem.ElevatorLevels.Level4));// If left trigger - // pressed, move to - // level 3. Else, go - // to top - - // Algae Arm Subsystem - m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar spin faster if A - // pressed, else stop - m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spit out ball if B - // pressed, else stop - m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spin slowly if Y - // pressed, else stop - // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) - // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); - // This command was taken out due to clashing and we already have a command that - // does the same thing - - // Command Compositions for Elevator + Coral - // Level 1 Preset - m_auxillaryController.a().onTrue(Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level1) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level1)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); - // Level 2 Preset - m_auxillaryController.b().onTrue(Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level2) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level2)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); - // Level 3 preset - m_auxillaryController.y().onTrue(Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level3) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level3)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); - // Level 4 preset - m_auxillaryController.x().onTrue(Commands.sequence( - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Level4) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Level4)), - m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - m_elevatorSubsystem.commandMoveToPreset(ElevatorLevels.Lowest) - .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorLevels.Highest)))); - - // Coral Arm Subsystem - m_driverController.povUp().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up));// Makes - // Coral - // Arm - // Go Up - m_driverController.povDown().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down));// Makes - // Coral - // Arm - // Go - // Down - m_driverController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, CoralArmLevels.Stop));// Stops - // Coral - // Arm - m_driverController.rightStick().onTrue(m_CoralArmSubsystem.emergencyStop());// Emergency Stop for Coral Arm(in case - // it goes past the top or bottom) Note: - // I don't know if the onTrue method - // will only run once, so test it before - // you use it - - // Drive Subsystem - // Code below is from swerve drive project generator - - // Note that X is defined as forward according to WPILib convention, - // and Y is defined as to the left according to WPILib convention. - drivetrain.setDefaultCommand( - // Drivetrain will execute this command periodically - drivetrain.applyRequest(() -> drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive forward - // with negative Y - // (forward) - .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) - .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise with - // negative X (left) - )); - m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); - m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point - .withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); - - // Run SysId routines when holding back/start and X/Y. - // Note that each routine should be run exactly once in a single log. - m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); - m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); - m_driverController.start().and(m_driverController.y()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); - m_driverController.start().and(m_driverController.x()).whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); - - // reset the field-centric heading on left bumper press - m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); - - drivetrain.addVisionMeasurement(LimelightHelpers.getBotPose2d("DriveCamera"), LimelightHelpers.get); - - // drivetrain.getModule(0). - - drivetrain.registerTelemetry(logger::telemeterize); - - } - - /** - * 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); - return autoChooser.getSelected(); - } + // The robot's subsystems and commands are defined here... + // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); + private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( + Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( + Constants.AlgaeArmConstants.algaeArmBarID); + private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); + private final CommandXboxController m_driverController = new CommandXboxController( + OperatorConstants.kDriverControllerPort); + + private final SendableChooser autoChooser; + /* Swerve drive platform setup */ // From Swerve Project Generator + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second + // max + // angular velocity + + /* Setting up bindings for necessary control of the swerve drive platform */ + private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) + .withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband + .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors + private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); + private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); + + private final Telemetry logger = new Telemetry(MaxSpeed); + + private final CommandXboxController m_auxillaryController = new CommandXboxController( + Constants.OperatorConstants.kAuxiliaryControllerPort); + + public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + + // End of Swerve Drive Platform setup + + /** + * The container for the robot. Contains subsystems, OI devices, and commands. + */ + public RobotContainer() { + + // Build an auto chooser. This will use Commands.none() as the default option. + autoChooser = AutoBuilder.buildAutoChooser(); + + // Another option that allows you to specify the default auto by its name + // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); + + SmartDashboard.putData("Auto Chooser", autoChooser); + + // Register named commands for use in autonomous routines + // NamedCommands.registerCommand("Example Command", + // m_exampleSubsystem.exampleMethodCommand()); + // NamedCommands.registerCommand("MoveElevatorToPresets", + // m_elevatorSubsystem.commandMoveToPreset(null)); + + // Configure the trigger bindings + configureBindings(Math.random()); + } + + /** + * 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}. + * + * @param random TODO + */ + private void configureBindings(double random) { + + // ================ Elevator Subsystem ================ // + + // Sets the default command for the elevator (the command that always runs) + // Checks if the left joystick is being moved, if it is, it will run the + // commandVoltage method with the left joystick's Y value + m_elevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) + ? m_elevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) + : m_elevatorSubsystem.commandVoltage(0)); + // 10 is a PLACEHOLDER value for now + + // The commands below make the elevator go to certain levels + m_auxillaryController.a().onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), m_elevatorSubsystem)); + m_auxillaryController.b().onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), m_elevatorSubsystem)); + m_auxillaryController.x().onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), m_elevatorSubsystem)); + m_auxillaryController.y().onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), m_elevatorSubsystem)); + + // ================ Algae Arm Subsystem ================ // + m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar spin faster if A + // pressed, else stop + m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spit out ball + // if B + // pressed, else stop + m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spin slowly if + // Y + // pressed, else stop + // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); + // This command was taken out due to clashing and we already have a command that + // does the same thing + + // Command Compositions for Elevator + Coral + // Level 1 Preset + // m_auxillaryController.a() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level1) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level1)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 2 Preset + // m_auxillaryController.b() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level2) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level2)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 3 preset + // m_auxillaryController.y() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level3) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level3)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 4 preset + // m_auxillaryController.x() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level4) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level4)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + + // ================ Coral Arm Subsystem ================ // + // makes coral arm go up + m_auxillaryController.leftBumper() + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up)); + // makes coral arm go down + m_auxillaryController.rightBumper() + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down)); + // stops coral arm + // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, + // CoralArmLevels.Stop)); + // Emergency Stop for Coral Arm (in case it goes past the top or bottom) Note: I + // don't know if the onTrue method will only run once, so test it before you use + // it + m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); + + // ================ Drive Subsystem ================ // + // Code below is from swerve drive project generator + + // Note that X is defined as forward according to WPILib convention, + // and Y is defined as to the left according to WPILib convention. + drivetrain.setDefaultCommand( + // Drivetrain will execute this command periodically + drivetrain.applyRequest(() -> drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive + // forward + // with + // negative + // Y + // (forward) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise + // with + // negative X (left) + )); + m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); + m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point + .withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); + + // Run SysId routines when holding back/start and X/Y. + // Note that each routine should be run exactly once in a single log. + m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + m_driverController.start().and(m_driverController.y()) + .whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + m_driverController.start().and(m_driverController.x()) + .whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + + // reset the field-centric heading on left bumper press + m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + + drivetrain.registerTelemetry(logger::telemeterize); + + } + + /** + * 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); + return autoChooser.getSelected(); + } } // another why not comment :) -// I do love some good comments :D \ No newline at end of file +// I do love some good comments :D diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 785a867..a88444f 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -1,4 +1,5 @@ package frc.robot.subsystems; + import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.Command; @@ -10,9 +11,9 @@ //import com.ctre.phoenix.Util; import com.ctre.phoenix6.hardware.TalonFX; import frc.robot.Utility; -import frc.robot.Constants.ElevatorConstants; +import frc.robot.Constants.ElevatorConstants; -public class ElevatorSubsystem extends SubsystemBase {//makes elevator subsystem into an actual subsystem +public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsystem into an actual subsystem // Motors private final TalonFX m_elevatorLeftMotor; @@ -22,68 +23,60 @@ public class ElevatorSubsystem extends SubsystemBase {//makes elevator subsystem private DigitalInput m_elevatorBottomLimitSwitch; // Encoder private final Encoder m_Encoder1; - // private TitanQuadEncoder m_elevatorEncoder; + // private TitanQuadEncoder m_elevatorEncoder; private double m_elevatorEncoder; // Placeholder - private ElevatorLevels currentElevatorState; - - - - - public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {//this is da place where stuff is actually initialized - m_elevatorEncoder = 0; // Placeholder - m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); - m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); - m_elevatorTopLimitSwitch = new DigitalInput(1); - m_elevatorBottomLimitSwitch = new DigitalInput(2); - m_Encoder1 = new Encoder(0, 1); - currentElevatorState = ElevatorLevels.Lowest; - } + private ElevatorPresets currentElevatorState; + + public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// this is da place where stuff is + // actually initialized + m_elevatorEncoder = 0; // Placeholder + m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); + m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); + m_elevatorTopLimitSwitch = new DigitalInput(1); + m_elevatorBottomLimitSwitch = new DigitalInput(2); + m_Encoder1 = new Encoder(0, 1); + currentElevatorState = ElevatorPresets.Lowest; + } -public enum ElevatorLevels { - Lowest, // bottom - Level1, // Levels of the reef - Level2, - Level3, - Level4, - Highest // top -} + public enum ElevatorPresets { + Lowest, // bottom + Level1, // Levels of the reef + Level2, Level3, Level4, Highest // top + } -//The commands below that are commented are currently not being used and have been replaced. They are kept just cause + // The commands below that are commented are currently not being used and have + // been replaced. They are kept just cause - //public Command raiseElevator() { - // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(0.1); - // }); - //} + // public Command raiseElevator() { + // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(0.1); + // }); + // } // public Command lowerElevator() { - // // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(-0.1); - // }); + // // Placeholder + // return runOnce( + // () -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(-0.1); + // }); // } - public boolean isElevatorAtTop() { // Placeholder if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { return false; - } - else if(m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()){ + } else if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()) { System.out.println("Error: Encoder And Top Limit Switch Mismatched"); return true; - } - else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + } else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { System.out.println("Error: Encoder And Top Limit Switch Mismatched"); return true; - } - else { + } else { return false; } } @@ -92,16 +85,15 @@ public boolean isElevatorAtBottom() { // Placeholder if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { return false; - } - else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && m_elevatorBottomLimitSwitch.get()){ + } else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue + && m_elevatorBottomLimitSwitch.get()) { System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); return true; - } - else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue + && !m_elevatorBottomLimitSwitch.get()) { System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); return true; - } - else { + } else { return false; } } @@ -109,89 +101,97 @@ else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue && !m public void setMotorElevatorSpeed(double voltage) { // Placeholder - // BEN: If this method truly will be run once (as above), then the stop conditions will only be evaluated once. + // BEN: If this method truly will be run once (as above), then the stop + // conditions will only be evaluated once. // We need to check the conditions continually somehow. - if (voltage > 0 && isElevatorAtTop()) {//if motor is moving and elevators at the top, make both move up + if (voltage > 0 && isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving up"); - } else if (voltage < 0 && isElevatorAtBottom()) {//or if its at bottom make move down + } else if (voltage < 0 && isElevatorAtBottom()) {// or if its at bottom make move down m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving down"); - } else {//otherwise just no speed + } else {// otherwise just no speed m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); Utility.printLn("Elevator is at the top or bottom and therefore the motors have been stopped"); } - + } - public void moveElevatorToPreset(ElevatorLevels desiredPreset) {//tells elevator where to go based on certain presets using distgusting switch case statements + public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevator where to go based on certain + // presets using distgusting switch case statements switch (desiredPreset) { - case Level1: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); - break; - case Level2: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); - break; - case Level3: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); - break; - case Level4: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); - break; + case Level1: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + break; + case Level2: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + break; + case Level3: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + break; + case Level4: + moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + break; } } + /** - * The moveElevatorToPosition method takes a double as a parameter. - * This value represents the number of rotations necessary to reach the target position. - * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a selective factorial. Yeah idk why I said that. Moving on + * The moveElevatorToPosition method takes a double as a parameter. This value + * represents the number of rotations necessary to reach the target position. + * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a + * selective factorial. Yeah idk why I said that. Moving on + * * @param position */ - public void moveElevatorToPosition (double position, ElevatorLevels desiredPreset) {//moves elevator to a certain position based on the encoder value + public void moveElevatorToPosition(double position, ElevatorPresets desiredPreset) {// moves elevator to a certain + // position based on the encoder + // value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! - if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down and makes sure the elevator isn't at the bottom + if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down + // and makes sure the elevator isn't at the bottom m_elevatorLeftMotor.set(-ElevatorConstants.kMotorElevatorSpeed); m_elevatorRightMotor.set(ElevatorConstants.kMotorElevatorSpeed); Utility.printLn("Elevator is moving down"); - } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move up and makes sure the elevator isn't at the top + } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move + // up and makes sure the elevator isn't at the + // top m_elevatorLeftMotor.set(ElevatorConstants.kMotorElevatorSpeed); m_elevatorRightMotor.set(-ElevatorConstants.kMotorElevatorSpeed); Utility.printLn("Elevator is moving up"); } else { - m_elevatorLeftMotor.set(0); + m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); - Utility.printLn("Elevator is at the top, bottom, desired position, or error occurred and therefore the motors have been stopped"); + Utility.printLn( + "Elevator is at the top, bottom, desired position, or error occurred and therefore the motors have been stopped"); currentElevatorState = desiredPreset; } } - public ElevatorLevels getElevatorState() { + public ElevatorPresets getElevatorState() { return currentElevatorState; } - public BooleanSupplier isElevatorAtDesiredState(ElevatorLevels desiredState) { + public BooleanSupplier isElevatorAtDesiredState(ElevatorPresets desiredState) { return () -> currentElevatorState == desiredState; } - public Command commandVoltage(double leftTrigger, double rightTrigger) {//just exists because just felt like it - return runOnce( - () -> { - setMotorElevatorSpeed(rightTrigger - leftTrigger); - } - ); + public Command commandVoltage(double voltage) {// just exists because just felt like it + return runOnce(() -> { + setMotorElevatorSpeed(voltage); + }); } - public Command commandMoveToPreset(ElevatorLevels desiredPreset) {//The first function in a very tall dependancy tree - return runOnce( - () -> { - moveElevatorToPreset(desiredPreset); - } - ); + public Command commandMoveToPreset(ElevatorPresets desiredPreset) {// The first function in a very tall dependancy + // tree + return runOnce(() -> { + moveElevatorToPreset(desiredPreset); + }); } - + } -//a comment because why not :) +// a comment because why not :) // This is a very cool comment *wink* *wink* *nudge* *nudge* \ No newline at end of file From 439379a9acb82c8bf190f3a6a5777138fb3061d4 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 11:02:26 -0600 Subject: [PATCH 12/32] merged feature-a --- .../robot/subsystems/ElevatorSubsystem.java | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index a88444f..3424aa6 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -3,7 +3,8 @@ import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.Command; -//import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import edu.wpi.first.wpilibj.Encoder; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj.Encoder; import java.util.function.BooleanSupplier; @@ -25,8 +26,12 @@ public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsyste private final Encoder m_Encoder1; // private TitanQuadEncoder m_elevatorEncoder; private double m_elevatorEncoder; // Placeholder + private ElevatorPresets currentElevatorState; + // Constants + private double motorElevatorSpeed; private ElevatorPresets currentElevatorState; + public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// this is da place where stuff is // actually initialized @@ -45,26 +50,27 @@ public enum ElevatorPresets { Level2, Level3, Level4, Highest // top } - // The commands below that are commented are currently not being used and have - // been replaced. They are kept just cause - - // public Command raiseElevator() { - // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(0.1); - // }); - // } - - // public Command lowerElevator() { - // // Placeholder - // return runOnce( - // () -> { - // /* one-time action goes here */ - // setMotorElevatorSpeed(-0.1); - // }); - // } + public Command raiseElevator() { + // Placeholder + return runOnce(() -> { + /* one-time action goes here */ + setMotorElevatorSpeed(0.1); + }); + } + + public Command lowerElevator() { + // Placeholder + return runOnce(() -> { + /* one-time action goes here */ + setMotorElevatorSpeed(-0.1); + }); + } + + public void debuggingMethod() { + SmartDashboard.putNumber("Elevator Encoder", m_Encoder1.get()); + SmartDashboard.putBoolean("Elevator Top Limit Switch", m_elevatorTopLimitSwitch.get()); + SmartDashboard.putBoolean("Elevator Bottom Limit Switch", m_elevatorBottomLimitSwitch.get()); + } public boolean isElevatorAtTop() { // Placeholder @@ -73,11 +79,8 @@ public boolean isElevatorAtTop() { } else if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()) { System.out.println("Error: Encoder And Top Limit Switch Mismatched"); return true; - } else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { - System.out.println("Error: Encoder And Top Limit Switch Mismatched"); - return true; } else { - return false; + return true; } } @@ -89,12 +92,8 @@ public boolean isElevatorAtBottom() { && m_elevatorBottomLimitSwitch.get()) { System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); return true; - } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderBottomValue - && !m_elevatorBottomLimitSwitch.get()) { - System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); - return true; } else { - return false; + return true; } } @@ -105,11 +104,12 @@ public void setMotorElevatorSpeed(double voltage) { // conditions will only be evaluated once. // We need to check the conditions continually somehow. - if (voltage > 0 && isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up + debuggingMethod(); + if (voltage > 0 && !isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving up"); - } else if (voltage < 0 && isElevatorAtBottom()) {// or if its at bottom make move down + } else if (voltage < 0 && !isElevatorAtBottom()) {// or if its at bottom make move down m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); Utility.printLn("Elevator is moving down"); @@ -151,17 +151,19 @@ public void moveElevatorToPosition(double position, ElevatorPresets desiredPrese // position based on the encoder // value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! - if (m_elevatorEncoder <= position && !isElevatorAtBottom()) { // checking to see if the motor wants to move down - // and makes sure the elevator isn't at the bottom - m_elevatorLeftMotor.set(-ElevatorConstants.kMotorElevatorSpeed); - m_elevatorRightMotor.set(ElevatorConstants.kMotorElevatorSpeed); + debuggingMethod(); + if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move + // down and makes sure the elevator isn't at + // the bottom + m_elevatorLeftMotor.set(-motorElevatorSpeed); + m_elevatorRightMotor.set(motorElevatorSpeed); Utility.printLn("Elevator is moving down"); - } else if (m_elevatorEncoder >= position && !isElevatorAtTop()) { // checking to see if the motor wants to move - // up and makes sure the elevator isn't at the - // top - m_elevatorLeftMotor.set(ElevatorConstants.kMotorElevatorSpeed); - m_elevatorRightMotor.set(-ElevatorConstants.kMotorElevatorSpeed); - Utility.printLn("Elevator is moving up"); + } else if (m_Encoder1.get() >= (position - 5) && !isElevatorAtTop()) { // checking to see if the motor wants to + // move up and makes sure the elevator + // isn't at the top + m_elevatorLeftMotor.set(motorElevatorSpeed); + m_elevatorRightMotor.set(-motorElevatorSpeed); + Utility.printLn("Elevator is moving up");1 } else { m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); @@ -194,4 +196,4 @@ public Command commandMoveToPreset(ElevatorPresets desiredPreset) {// The first } // a comment because why not :) -// This is a very cool comment *wink* *wink* *nudge* *nudge* \ No newline at end of file +// This is a very cool comment *wink* *wink* *nudge* *nudge* From 66fd000103bd609b3e0d325be89227265cd3c575 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 13:39:29 -0600 Subject: [PATCH 13/32] Fixed some elevator errors after the merge -H --- .../robot/subsystems/ElevatorSubsystem.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 3424aa6..987931f 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -30,9 +30,6 @@ public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsyste // Constants private double motorElevatorSpeed; - private ElevatorPresets currentElevatorState; - - public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// this is da place where stuff is // actually initialized m_elevatorEncoder = 0; // Placeholder @@ -41,13 +38,11 @@ public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// m_elevatorTopLimitSwitch = new DigitalInput(1); m_elevatorBottomLimitSwitch = new DigitalInput(2); m_Encoder1 = new Encoder(0, 1); - currentElevatorState = ElevatorPresets.Lowest; + currentElevatorState = ElevatorPresets.Level1; } - public enum ElevatorPresets { - Lowest, // bottom - Level1, // Levels of the reef - Level2, Level3, Level4, Highest // top + public enum ElevatorPresets { // different levels for the coral reef + Level1, Level2, Level3, Level4 } public Command raiseElevator() { @@ -99,11 +94,6 @@ public boolean isElevatorAtBottom() { public void setMotorElevatorSpeed(double voltage) { // Placeholder - - // BEN: If this method truly will be run once (as above), then the stop - // conditions will only be evaluated once. - // We need to check the conditions continually somehow. - debuggingMethod(); if (voltage > 0 && !isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); @@ -142,10 +132,15 @@ public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevat /** * The moveElevatorToPosition method takes a double as a parameter. This value * represents the number of rotations necessary to reach the target position. + * * fun fact: 6!! is 6 * 4 * 2 while 7!! is 7 * 5 * 3 * 1. Kind of like a * selective factorial. Yeah idk why I said that. Moving on * + * Oooooo thats a really fun fact! I didn't know you could have selective + * factorials! - H + * * @param position + * @param desiredPreset */ public void moveElevatorToPosition(double position, ElevatorPresets desiredPreset) {// moves elevator to a certain // position based on the encoder @@ -163,7 +158,7 @@ public void moveElevatorToPosition(double position, ElevatorPresets desiredPrese // isn't at the top m_elevatorLeftMotor.set(motorElevatorSpeed); m_elevatorRightMotor.set(-motorElevatorSpeed); - Utility.printLn("Elevator is moving up");1 + Utility.printLn("Elevator is moving up"); } else { m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); From c5407437a63730b18185d5b686d688932e4ab84e Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 13:40:19 -0600 Subject: [PATCH 14/32] Removed some example classes that were left from creating the project -H --- .../main/java/frc/robot/RobotContainer.java | 417 +++++++++--------- .../main/java/frc/robot/commands/Autos.java | 20 - .../frc/robot/commands/ExampleCommand.java | 43 -- .../robot/subsystems/ExampleSubsystem.java | 47 -- 4 files changed, 219 insertions(+), 308 deletions(-) delete mode 100644 2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java delete mode 100644 2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java delete mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 0f340f5..eb5a952 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -45,9 +45,7 @@ import java.lang.Math; import frc.robot.Constants.OperatorConstants; import frc.robot.generated.TunerConstants; -import frc.robot.commands.ExampleCommand; import frc.robot.subsystems.ElevatorSubsystem; -import frc.robot.subsystems.ExampleSubsystem; import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; import frc.robot.subsystems.AlgaeArmSubsystem; @@ -59,7 +57,6 @@ import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; @@ -72,226 +69,250 @@ * 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(); - private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( - Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); - private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( - Constants.AlgaeArmConstants.algaeArmBarID); - private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(Constants.CoralArmConstants.coralArmID); - private final CommandXboxController m_driverController = new CommandXboxController( - OperatorConstants.kDriverControllerPort); + // The robot's subsystems and commands are defined here... + // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); + private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( + Constants.ElevatorConstants.kElevatorLeftMotorPort, + Constants.ElevatorConstants.kElevatorRightMotorPort); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( + Constants.AlgaeArmConstants.algaeArmBarID); + private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem( + Constants.CoralArmConstants.coralArmID); + private final CommandXboxController m_driverController = new CommandXboxController( + OperatorConstants.kDriverControllerPort); - private final SendableChooser autoChooser; - /* Swerve drive platform setup */ // From Swerve Project Generator - private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top speed - private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per second - // max - // angular velocity + private final SendableChooser autoChooser; + /* Swerve drive platform setup */ // From Swerve Project Generator + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top + // speed + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per + // second + // max + // angular velocity - /* Setting up bindings for necessary control of the swerve drive platform */ - private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) - .withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband - .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive motors - private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); - private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); + /* Setting up bindings for necessary control of the swerve drive platform */ + private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) + .withRotationalDeadband(MaxAngularRate * 0.1) // Add a 10% deadband + .withDriveRequestType(DriveRequestType.OpenLoopVoltage); // Use open-loop control for drive + // motors + private final SwerveRequest.SwerveDriveBrake brake = new SwerveRequest.SwerveDriveBrake(); + private final SwerveRequest.PointWheelsAt point = new SwerveRequest.PointWheelsAt(); - private final Telemetry logger = new Telemetry(MaxSpeed); + private final Telemetry logger = new Telemetry(MaxSpeed); - private final CommandXboxController m_auxillaryController = new CommandXboxController( - Constants.OperatorConstants.kAuxiliaryControllerPort); + private final CommandXboxController m_auxillaryController = new CommandXboxController( + Constants.OperatorConstants.kAuxiliaryControllerPort); - public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); - // End of Swerve Drive Platform setup + // End of Swerve Drive Platform setup - /** - * The container for the robot. Contains subsystems, OI devices, and commands. - */ - public RobotContainer() { + /** + * The container for the robot. Contains subsystems, OI devices, and commands. + */ + public RobotContainer() { - // Build an auto chooser. This will use Commands.none() as the default option. - autoChooser = AutoBuilder.buildAutoChooser(); + // Build an auto chooser. This will use Commands.none() as the default option. + autoChooser = AutoBuilder.buildAutoChooser(); - // Another option that allows you to specify the default auto by its name - // autoChooser = AutoBuilder.buildAutoChooser("My Default Auto"); + SmartDashboard.putData("Auto Chooser", autoChooser); - SmartDashboard.putData("Auto Chooser", autoChooser); + // Register named commands for use in autonomous routines + // NamedCommands.registerCommand("Example Command", + // m_exampleSubsystem.exampleMethodCommand()); + // NamedCommands.registerCommand("MoveElevatorToPresets", + // m_elevatorSubsystem.commandMoveToPreset(null)); - // Register named commands for use in autonomous routines - // NamedCommands.registerCommand("Example Command", - // m_exampleSubsystem.exampleMethodCommand()); - // NamedCommands.registerCommand("MoveElevatorToPresets", - // m_elevatorSubsystem.commandMoveToPreset(null)); + // Configure the trigger bindings + configureBindings(Math.random()); + } - // Configure the trigger bindings - configureBindings(Math.random()); - } + /** + * 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}. + * + * @param random TODO + */ + private void configureBindings(double random) { - /** - * 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}. - * - * @param random TODO - */ - private void configureBindings(double random) { + // ================ Elevator Subsystem ================ // - // ================ Elevator Subsystem ================ // + // Sets the default command for the elevator (the command that always runs) + // Checks if the left joystick is being moved, if it is, it will run the + // commandVoltage method with the left joystick's Y value + m_elevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) + ? m_elevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) + : m_elevatorSubsystem.commandVoltage(0)); + // 10 is a PLACEHOLDER value for now - // Sets the default command for the elevator (the command that always runs) - // Checks if the left joystick is being moved, if it is, it will run the - // commandVoltage method with the left joystick's Y value - m_elevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) - ? m_elevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) - : m_elevatorSubsystem.commandVoltage(0)); - // 10 is a PLACEHOLDER value for now + // The commands below make the elevator go to certain levels + m_auxillaryController.a() + .onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), + m_elevatorSubsystem)); + m_auxillaryController.b() + .onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), + m_elevatorSubsystem)); + m_auxillaryController.x() + .onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), + m_elevatorSubsystem)); + m_auxillaryController.y() + .onTrue(new RunCommand( + () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), + m_elevatorSubsystem)); - // The commands below make the elevator go to certain levels - m_auxillaryController.a().onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), m_elevatorSubsystem)); - m_auxillaryController.b().onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), m_elevatorSubsystem)); - m_auxillaryController.x().onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), m_elevatorSubsystem)); - m_auxillaryController.y().onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), m_elevatorSubsystem)); + // ================ Algae Arm Subsystem ================ // + m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar + // spin faster if + // A + // pressed, else + // stop + m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar + // spit out ball + // if B + // pressed, else + // stop + m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) + .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar + // spin slowly if + // Y + // pressed, else + // stop + // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); + // This command was taken out due to clashing and we already have a command that + // does the same thing - // ================ Algae Arm Subsystem ================ // - m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar spin faster if A - // pressed, else stop - m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spit out ball - // if B - // pressed, else stop - m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar spin slowly if - // Y - // pressed, else stop - // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) - // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); - // This command was taken out due to clashing and we already have a command that - // does the same thing + // Command Compositions for Elevator + Coral + // Level 1 Preset + // m_auxillaryController.a() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level1) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level1)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 2 Preset + // m_auxillaryController.b() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level2) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level2)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 3 preset + // m_auxillaryController.y() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level3) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level3)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // // Level 4 preset + // m_auxillaryController.x() + // .onTrue(Commands.sequence( + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level4) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level4)), + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), + // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), + // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) + // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); - // Command Compositions for Elevator + Coral - // Level 1 Preset - // m_auxillaryController.a() - // .onTrue(Commands.sequence( - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level1) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level1)), - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); - // // Level 2 Preset - // m_auxillaryController.b() - // .onTrue(Commands.sequence( - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level2) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level2)), - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); - // // Level 3 preset - // m_auxillaryController.y() - // .onTrue(Commands.sequence( - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level3) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level3)), - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); - // // Level 4 preset - // m_auxillaryController.x() - // .onTrue(Commands.sequence( - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level4) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level4)), - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // ================ Coral Arm Subsystem ================ // + // makes coral arm go up + m_auxillaryController.leftBumper() + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up)); + // makes coral arm go down + m_auxillaryController.rightBumper() + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down)); + // stops coral arm + // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, + // CoralArmLevels.Stop)); + // Emergency Stop for Coral Arm (in case it goes past the top or bottom) Note: I + // don't know if the onTrue method will only run once, so test it before you use + // it + m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); - // ================ Coral Arm Subsystem ================ // - // makes coral arm go up - m_auxillaryController.leftBumper() - .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up)); - // makes coral arm go down - m_auxillaryController.rightBumper() - .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down)); - // stops coral arm - // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, - // CoralArmLevels.Stop)); - // Emergency Stop for Coral Arm (in case it goes past the top or bottom) Note: I - // don't know if the onTrue method will only run once, so test it before you use - // it - m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); + // ================ Drive Subsystem ================ // + // Code below is from swerve drive project generator - // ================ Drive Subsystem ================ // - // Code below is from swerve drive project generator + // Note that X is defined as forward according to WPILib convention, + // and Y is defined as to the left according to WPILib convention. + drivetrain.setDefaultCommand( + // Drivetrain will execute this command periodically + drivetrain.applyRequest(() -> drive + .withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive + // forward + // with + // negative + // Y + // (forward) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left + // with + // negative X + // (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive + // counterclockwise + // with + // negative + // X + // (left) + )); + m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); + m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point.withModuleDirection( + new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); - // Note that X is defined as forward according to WPILib convention, - // and Y is defined as to the left according to WPILib convention. - drivetrain.setDefaultCommand( - // Drivetrain will execute this command periodically - drivetrain.applyRequest(() -> drive.withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive - // forward - // with - // negative - // Y - // (forward) - .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left with negative X (left) - .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive counterclockwise - // with - // negative X (left) - )); - m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); - m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point - .withModuleDirection(new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); + // Run SysId routines when holding back/start and X/Y. + // Note that each routine should be run exactly once in a single log. + m_driverController.back().and(m_driverController.y()) + .whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + m_driverController.back().and(m_driverController.x()) + .whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + m_driverController.start().and(m_driverController.y()) + .whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + m_driverController.start().and(m_driverController.x()) + .whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); - // Run SysId routines when holding back/start and X/Y. - // Note that each routine should be run exactly once in a single log. - m_driverController.back().and(m_driverController.y()).whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); - m_driverController.back().and(m_driverController.x()).whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); - m_driverController.start().and(m_driverController.y()) - .whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); - m_driverController.start().and(m_driverController.x()) - .whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + // reset the field-centric heading on left bumper press + m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); - // reset the field-centric heading on left bumper press - m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + drivetrain.registerTelemetry(logger::telemeterize); - drivetrain.registerTelemetry(logger::telemeterize); + } - } - - /** - * 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); - return autoChooser.getSelected(); - } + /** + * 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(); + } } // another why not comment :) // I do love some good comments :D diff --git a/2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java b/2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java deleted file mode 100644 index 107aad7..0000000 --- a/2024-2025/main-bot/src/main/java/frc/robot/commands/Autos.java +++ /dev/null @@ -1,20 +0,0 @@ -// 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/2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java b/2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java deleted file mode 100644 index 7481d3c..0000000 --- a/2024-2025/main-bot/src/main/java/frc/robot/commands/ExampleCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -// 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"}) - 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/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java deleted file mode 100644 index 6b375da..0000000 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ExampleSubsystem.java +++ /dev/null @@ -1,47 +0,0 @@ -// 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 - } -} From 2bfb47796a3a4835a270b6698c07208dd112e0d5 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 14:02:46 -0600 Subject: [PATCH 15/32] Changed some controls to better fit Vy's preferences, also adjusted the AlgaeArmSubsystem file to use StartEnd Commands -H --- .../main/java/frc/robot/RobotContainer.java | 81 ++++++++--------- .../robot/subsystems/AlgaeArmSubsystem.java | 87 ++++++++++--------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index eb5a952..27707fd 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -141,7 +141,8 @@ public RobotContainer() { */ private void configureBindings(double random) { - // ================ Elevator Subsystem ================ // + // ============================ Elevator Subsystem ============================ + // // // Sets the default command for the elevator (the command that always runs) // Checks if the left joystick is being moved, if it is, it will run the @@ -169,29 +170,34 @@ private void configureBindings(double random) { () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), m_elevatorSubsystem)); - // ================ Algae Arm Subsystem ================ // - m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, false, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algaeBar - // spin faster if - // A - // pressed, else - // stop - m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, true, false)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar - // spit out ball - // if B - // pressed, else - // stop - m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, true)) - .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false));// makes algae bar - // spin slowly if - // Y - // pressed, else - // stop - // m_auxillaryController.a().whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) - // .onFalse(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)); - // This command was taken out due to clashing and we already have a command that - // does the same thing + // ====================== Algae Arm Subsystem ====================== // + + m_auxillaryController.leftTrigger() + // makes algae bar intake if LT pressed + .whileTrue(m_AlgaeArmSubsystem.commandAlgaeIntake(m_AlgaeArmSubsystem)) + // Interrupts the command, causing motors to SLOWLY SPIN INWARDS (this way they + // can maintain control of the algae) + .onFalse(m_AlgaeArmSubsystem.commandInterrupt()); + m_auxillaryController.rightTrigger() + // makes algae bar outtake if RT pressed + .whileTrue(m_AlgaeArmSubsystem.commandAlgaeOuttake(m_AlgaeArmSubsystem)) + // interrupts the command, causing motors to STOP + .onFalse(m_AlgaeArmSubsystem.commandInterrupt()); + + // The code below was taken out to streamline the Algae arm (we need two buttons + // to control the algae arm, the method below uses 3 buttons) + // // makes algae bar spin faster if A pressed, else stop + // m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, + // false, false)) + // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); + // // makes algae bar spit out ball if B pressed, else stop + // m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, + // true, false)) + // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); + // // makes algae bar spin slowly if Y pressed, else stop + // m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, + // false, true)) + // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); // Command Compositions for Elevator + Coral // Level 1 Preset @@ -243,7 +249,7 @@ private void configureBindings(double random) { // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); - // ================ Coral Arm Subsystem ================ // + // ====================== Coral Arm Subsystem ====================== // // makes coral arm go up m_auxillaryController.leftBumper() .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up)); @@ -258,7 +264,7 @@ private void configureBindings(double random) { // it m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); - // ================ Drive Subsystem ================ // + // ====================== Drive Subsystem ====================== // // Code below is from swerve drive project generator // Note that X is defined as forward according to WPILib convention, @@ -266,23 +272,12 @@ private void configureBindings(double random) { drivetrain.setDefaultCommand( // Drivetrain will execute this command periodically drivetrain.applyRequest(() -> drive - .withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive - // forward - // with - // negative - // Y - // (forward) - .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive left - // with - // negative X - // (left) - .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate) // Drive - // counterclockwise - // with - // negative - // X - // (left) - )); + // Drive forward with negative Y (forward) + .withVelocityX(-m_driverController.getLeftY() * MaxSpeed) + // Drive left with negative X (left) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) + // Drive counterclockwise with negative X (left) + .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate))); m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point.withModuleDirection( new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java index cd75d4c..78cf0b4 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java @@ -9,60 +9,65 @@ import edu.wpi.first.wpilibj2.command.SubsystemBase; //import edu.wpi.first.wpilibj2.command.button.CommandXboxController; import edu.wpi.first.wpilibj2.command.Command; -//import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.Commands; public class AlgaeArmSubsystem extends SubsystemBase { - //Motors - private final TalonSRX m_algaeBarMove; + // Motors + private final TalonSRX m_algaeBarMotor; - //Initialized Stuff + // Initialized Stuff public AlgaeArmSubsystem(int algaeBarMoveID) { - m_algaeBarMove = new TalonSRX(algaeBarMoveID); + m_algaeBarMotor = new TalonSRX(algaeBarMoveID); } - public Command commandMakeBarSpin(boolean APressed, boolean BPressed, boolean YPressed) {//makes, well, the bar spin - return runOnce( - () -> { - if (APressed) { - setAlgaeArmVoltage(0.5);//מציין מיקום man it sure is nice to hide secret messages in hebrew hungarian and albanian in that specific order - } - else if(BPressed) { - setAlgaeArmVoltage(-0.25);//helyőrző - } - else if (YPressed){ - setAlgaeArmVoltage(0.25);//vendmbajtes - } - else { - setAlgaeArmVoltage(0); - } + + public Command commandMakeBarSpin(boolean APressed, boolean BPressed, boolean YPressed) {// makes, well, the bar + // spin + return runOnce(() -> { + if (APressed) { + setAlgaeArmVoltage(0.5);// מציין מיקום man it sure is nice to hide secret messages in hebrew hungarian + // and albanian in that specific order + } else if (BPressed) { + setAlgaeArmVoltage(-0.25);// helyőrző + } else if (YPressed) { + setAlgaeArmVoltage(0.25);// vendmbajtes + } else { + setAlgaeArmVoltage(0); } - ); + }); } - public void setAlgaeArmVoltage(double voltage) {//place to move da algae bar - m_algaeBarMove.set(ControlMode.PercentOutput, voltage);//moves da algae bar + + public void setAlgaeArmVoltage(double voltage) {// place to move da algae bar + m_algaeBarMotor.set(ControlMode.PercentOutput, voltage);// moves da algae bar } + public Command PrintRand() { - return runOnce( - () -> { + return runOnce(() -> { System.out.println(Math.random() * 100); - }//a curly brace - );//a parenthesis followed by a semicolon - }//a curly brace + }// a curly brace + );// a parenthesis followed by a semicolon + }// a curly brace - //who placed these here? They lowkey clash wittewawy -_- - /*public Command commandAlgaeIntake(AlgaeArmSubsystem algaeArmSubsystem) { - return Commands.startEnd( - () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0.3), //placeholder - () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0.1), // placeholder - algaeArmSubsystem); + // who placed these here? They lowkey clash wittewawy -_- + public Command commandAlgaeIntake(AlgaeArmSubsystem algaeArmSubsystem) { + return Commands.startEnd(() -> m_algaeBarMotor.set(ControlMode.PercentOutput, 0.3), // placeholder + () -> m_algaeBarMotor.set(ControlMode.PercentOutput, 0.1), // placeholder + algaeArmSubsystem); } public Command commandAlgaeOuttake(AlgaeArmSubsystem algaeArmSubsystem) { - return Commands.startEnd( - () -> m_algaeBarMove.set(ControlMode.PercentOutput, -0.3), //placeholder - () -> m_algaeBarMove.set(ControlMode.PercentOutput, 0), //placeholder - algaeArmSubsystem); - }*/ + return Commands.startEnd(() -> m_algaeBarMotor.set(ControlMode.PercentOutput, -0.3), // placeholder + () -> m_algaeBarMotor.set(ControlMode.PercentOutput, 0), // placeholder + algaeArmSubsystem); + } + + public Command commandInterrupt() { + return runOnce(() -> { + ; + // This command has not other purpose than to interrupt the algae arm + // Causing the end conditions of the .startEnd() commands to be met + }); + } - -}//a curly brace \ No newline at end of file +} +// a curly brace \ No newline at end of file From 3a224e7b7b5f2366eaca76e49d98bcb12065c7a2 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 15:19:13 -0600 Subject: [PATCH 16/32] Got rid of test encoder in Elevator Subsystem, plus some mic. changes to sim settings -H --- 2024-2025/main-bot/simgui-ds.json | 5 +++++ 2024-2025/main-bot/simgui-window.json | 2 +- .../main/java/frc/robot/subsystems/ElevatorSubsystem.java | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/2024-2025/main-bot/simgui-ds.json b/2024-2025/main-bot/simgui-ds.json index 73cc713..4a63cc1 100644 --- a/2024-2025/main-bot/simgui-ds.json +++ b/2024-2025/main-bot/simgui-ds.json @@ -1,4 +1,9 @@ { + "System Joysticks": { + "window": { + "enabled": false + } + }, "keyboardJoysticks": [ { "axisConfig": [ diff --git a/2024-2025/main-bot/simgui-window.json b/2024-2025/main-bot/simgui-window.json index 391c753..904d022 100644 --- a/2024-2025/main-bot/simgui-window.json +++ b/2024-2025/main-bot/simgui-window.json @@ -57,7 +57,7 @@ "###Timing": { "Collapsed": "0", "Pos": "5,150", - "Size": "135,173" + "Size": "135,150" }, "Debug##Default": { "Collapsed": "0", diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 987931f..b32b47e 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -24,15 +24,12 @@ public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsyste private DigitalInput m_elevatorBottomLimitSwitch; // Encoder private final Encoder m_Encoder1; - // private TitanQuadEncoder m_elevatorEncoder; - private double m_elevatorEncoder; // Placeholder private ElevatorPresets currentElevatorState; // Constants private double motorElevatorSpeed; public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// this is da place where stuff is // actually initialized - m_elevatorEncoder = 0; // Placeholder m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); m_elevatorTopLimitSwitch = new DigitalInput(1); From ad9fe3366d460ca93fd1211f93cc1cea6f3ce1b4 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 15:20:10 -0600 Subject: [PATCH 17/32] Added some of the basics of Maple Sim (error popping up tho with normal simulation) -H --- .../src/main/java/frc/robot/Constants.java | 32 +++++-- .../src/main/java/frc/robot/Robot.java | 86 +++++++++++-------- .../main/java/frc/robot/RobotContainer.java | 57 ++++++------ .../robot/subsystems/MapleSimSubsystem.java | 54 ++++++++++++ 2024-2025/main-bot/vendordeps/maple-sim.json | 26 ++++++ 5 files changed, 184 insertions(+), 71 deletions(-) create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java create mode 100644 2024-2025/main-bot/vendordeps/maple-sim.json diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java index 809d1d2..c67b70a 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java @@ -5,18 +5,21 @@ 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. + * 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. + *

+ * 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; public static final int kAuxiliaryControllerPort = 1; } + // PLACEHOLDER IDS!!! public static class ElevatorConstants { public static final int kElevatorLeftMotorPort = 12; @@ -33,11 +36,26 @@ public static class ElevatorPreset { public static final double level4EncoderValue = 2.0; } } + + public static class DrivetrainConstants { + public static final double gearRatio = 4.59375; + public static final double lengthBetweenSwerveModules = 22.5; + public static final double lengthOfBumpers = 25.0; // placeholder + + // Motor and encoder ids (ALL ARE PLACEHOLDERS. FIND ACTUAL IDS IN + // COMMANDSWERVEDRIVETRAIN.JAVA) + public static final int FLDriveMotorID = 0; + public static final int FLTurnMotorID = 1; + // You can finish this out Scott! :D + + } + public static final class AlgaeArmConstants { - public static final int algaeArmBarID = 8;//placeholder + public static final int algaeArmBarID = 8;// placeholder } + public static class CoralArmConstants { - public static final int coralArmID = 69;//placeholder with funny number hehe + public static final int coralArmID = 69;// placeholder with funny number hehe public static final int kCoralEncoderTopValue = 1000; public static final int kCoralEncoderTopBuffer = kCoralEncoderTopValue + 10; public static final int kCoralEncoderBottomValue = 0; diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java index f43ffad..a4e3a30 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java @@ -1,24 +1,15 @@ // PROGRAMMING TO DOS! // Basic TO DOs - // Set up Elevator simulation or Algae arm simulation in test bed // Status: Failed +// Set up Elevator simulation or Algae arm simulation in test bed // Status: Failed // Advanced TO DOs - //NONE +//NONE // To-Dos: Once the elevator is attached - // Move elevator and update encoder preset values in Constants.java // Status: Not Started - // Check which motors need to be reversed// Status: Not Started - // Investigate Motion Magic // Status: Not Started - - - - - - - - - +// Move elevator and update encoder preset values in Constants.java // Status: Not Started +// Check which motors need to be reversed// Status: Not Started +// Investigate Motion Magic // Status: Not Started // Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of @@ -26,6 +17,8 @@ package frc.robot; +import org.ironmaple.simulation.SimulatedArena; + import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj2.command.Command; @@ -33,50 +26,61 @@ 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. + * 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; /** - * This function is run when the robot is first started up and should be used for any - * initialization code. + * This function is run when the robot is first started up and should be used + * for any initialization code. */ - public Robot() {//instantiate robot container - + public Robot() {// instantiate robot container + 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 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. + *

+ * 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 + // 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() {} + public void disabledInit() { + } @Override - public void disabledPeriodic() {} + public void disabledPeriodic() { + } - /** This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ + /** + * This autonomous runs the autonomous command selected by your + * {@link RobotContainer} class. + */ @Override public void autonomousInit() { m_autonomousCommand = m_robotContainer.getAutonomousCommand(); @@ -89,7 +93,8 @@ public void autonomousInit() { /** This function is called periodically during autonomous. */ @Override - public void autonomousPeriodic() {} + public void autonomousPeriodic() { + } @Override public void teleopInit() { @@ -104,7 +109,8 @@ public void teleopInit() { /** This function is called periodically during operator control. */ @Override - public void teleopPeriodic() {} + public void teleopPeriodic() { + } @Override public void testInit() { @@ -114,13 +120,17 @@ public void testInit() { /** This function is called periodically during test mode. */ @Override - public void testPeriodic() {} + public void testPeriodic() { + } /** This function is called once when the robot is first started up. */ @Override - public void simulationInit() {} + public void simulationInit() { + } /** This function is called periodically whilst in simulation. */ @Override - public void simulationPeriodic() {} + public void simulationPeriodic() { + SimulatedArena.getInstance().simulationPeriodic(); + } } \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 27707fd..39d5b4f 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -46,6 +46,8 @@ import frc.robot.Constants.OperatorConstants; import frc.robot.generated.TunerConstants; import frc.robot.subsystems.ElevatorSubsystem; +import frc.robot.subsystems.LimeLightSubsystem; +import frc.robot.subsystems.MapleSimSubsystem; import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; import frc.robot.subsystems.AlgaeArmSubsystem; @@ -71,7 +73,7 @@ public class RobotContainer { // The robot's subsystems and commands are defined here... // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); - private final ElevatorSubsystem m_elevatorSubsystem = new ElevatorSubsystem( + private final ElevatorSubsystem m_ElevatorSubsystem = new ElevatorSubsystem( Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( @@ -80,6 +82,8 @@ public class RobotContainer { Constants.CoralArmConstants.coralArmID); private final CommandXboxController m_driverController = new CommandXboxController( OperatorConstants.kDriverControllerPort); + private final CommandXboxController m_auxillaryController = new CommandXboxController( + Constants.OperatorConstants.kAuxiliaryControllerPort); private final SendableChooser autoChooser; /* Swerve drive platform setup */ // From Swerve Project Generator @@ -100,10 +104,10 @@ public class RobotContainer { private final Telemetry logger = new Telemetry(MaxSpeed); - private final CommandXboxController m_auxillaryController = new CommandXboxController( - Constants.OperatorConstants.kAuxiliaryControllerPort); - - public final CommandSwerveDrivetrain drivetrain = TunerConstants.createDrivetrain(); + public final CommandSwerveDrivetrain m_DrivetrainSubsystem = TunerConstants.createDrivetrain(); + // The following subsystems must be created after the drivetrain + public final LimeLightSubsystem m_LightSubsystem = new LimeLightSubsystem(m_DrivetrainSubsystem); + public final MapleSimSubsystem m_MapleSimSubsystem = new MapleSimSubsystem(m_DrivetrainSubsystem); // End of Swerve Drive Platform setup @@ -147,28 +151,28 @@ private void configureBindings(double random) { // Sets the default command for the elevator (the command that always runs) // Checks if the left joystick is being moved, if it is, it will run the // commandVoltage method with the left joystick's Y value - m_elevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) - ? m_elevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) - : m_elevatorSubsystem.commandVoltage(0)); + m_ElevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) + ? m_ElevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) + : m_ElevatorSubsystem.commandVoltage(0)); // 10 is a PLACEHOLDER value for now // The commands below make the elevator go to certain levels m_auxillaryController.a() .onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), - m_elevatorSubsystem)); + () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), + m_ElevatorSubsystem)); m_auxillaryController.b() .onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), - m_elevatorSubsystem)); + () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), + m_ElevatorSubsystem)); m_auxillaryController.x() .onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), - m_elevatorSubsystem)); + () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), + m_ElevatorSubsystem)); m_auxillaryController.y() .onTrue(new RunCommand( - () -> m_elevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), - m_elevatorSubsystem)); + () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), + m_ElevatorSubsystem)); // ====================== Algae Arm Subsystem ====================== // @@ -269,34 +273,35 @@ private void configureBindings(double random) { // Note that X is defined as forward according to WPILib convention, // and Y is defined as to the left according to WPILib convention. - drivetrain.setDefaultCommand( + m_DrivetrainSubsystem.setDefaultCommand( // Drivetrain will execute this command periodically - drivetrain.applyRequest(() -> drive + m_DrivetrainSubsystem.applyRequest(() -> drive // Drive forward with negative Y (forward) .withVelocityX(-m_driverController.getLeftY() * MaxSpeed) // Drive left with negative X (left) .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) // Drive counterclockwise with negative X (left) .withRotationalRate(-m_driverController.getRightX() * MaxAngularRate))); - m_driverController.a().whileTrue(drivetrain.applyRequest(() -> brake)); - m_driverController.b().whileTrue(drivetrain.applyRequest(() -> point.withModuleDirection( + m_driverController.a().whileTrue(m_DrivetrainSubsystem.applyRequest(() -> brake)); + m_driverController.b().whileTrue(m_DrivetrainSubsystem.applyRequest(() -> point.withModuleDirection( new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); // Run SysId routines when holding back/start and X/Y. // Note that each routine should be run exactly once in a single log. m_driverController.back().and(m_driverController.y()) - .whileTrue(drivetrain.sysIdDynamic(Direction.kForward)); + .whileTrue(m_DrivetrainSubsystem.sysIdDynamic(Direction.kForward)); m_driverController.back().and(m_driverController.x()) - .whileTrue(drivetrain.sysIdDynamic(Direction.kReverse)); + .whileTrue(m_DrivetrainSubsystem.sysIdDynamic(Direction.kReverse)); m_driverController.start().and(m_driverController.y()) - .whileTrue(drivetrain.sysIdQuasistatic(Direction.kForward)); + .whileTrue(m_DrivetrainSubsystem.sysIdQuasistatic(Direction.kForward)); m_driverController.start().and(m_driverController.x()) - .whileTrue(drivetrain.sysIdQuasistatic(Direction.kReverse)); + .whileTrue(m_DrivetrainSubsystem.sysIdQuasistatic(Direction.kReverse)); // reset the field-centric heading on left bumper press - m_driverController.leftBumper().onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric())); + m_driverController.leftBumper() + .onTrue(m_DrivetrainSubsystem.runOnce(() -> m_DrivetrainSubsystem.seedFieldCentric())); - drivetrain.registerTelemetry(logger::telemeterize); + m_DrivetrainSubsystem.registerTelemetry(logger::telemeterize); } diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java new file mode 100644 index 0000000..60463da --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java @@ -0,0 +1,54 @@ +package frc.robot.subsystems; + +import org.ironmaple.simulation.SimulatedArena; +import org.ironmaple.simulation.drivesims.COTS; +import org.ironmaple.simulation.drivesims.configs.DriveTrainSimulationConfig; +import org.ironmaple.simulation.seasonspecific.crescendo2024.CrescendoNoteOnField; + +import com.ctre.phoenix6.swerve.SwerveDrivetrain; +import edu.wpi.first.units.Units; + +import edu.wpi.first.math.geometry.Translation2d; +import edu.wpi.first.math.system.plant.DCMotor; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +import frc.robot.Constants.DrivetrainConstants;; + +public class MapleSimSubsystem extends SubsystemBase { + + private DriveTrainSimulationConfig driveTrainSimulationConfig; + + public MapleSimSubsystem(CommandSwerveDrivetrain drivetrain) { + // Placeholder + + // Obtains the default instance of the simulation world, which is a Crescendo + // Arena. + // SimulatedArena.getInstance(); + // Overrides the default simulation (isn't supported right now) + // SimulatedArena.overrideInstance(SimulatedArena newInstance); + + // Create and configure a drivetrain simulation configuration + driveTrainSimulationConfig = DriveTrainSimulationConfig.Default() + // Specify gyro type (for realistic gyro drifting and error simulation) + .withGyro(COTS.ofPigeon2()) + // Specify swerve module (for realistic swerve dynamics) + .withSwerveModule(COTS.ofSwerveXFlipped(DCMotor.getKrakenX60(1), // Drive motor is a Kraken X60 + DCMotor.getKrakenX60(1), // Steer motor is a Kraken + COTS.WHEELS.DEFAULT_NEOPRENE_TREAD.cof, // Use the COF for Neoprene Wheels + 9)) // L3 Gear ratio + // Configures the track length and track width (spacing between swerve modules) + .withTrackLengthTrackWidth(Units.Inches.of(DrivetrainConstants.lengthBetweenSwerveModules), + Units.Inches.of(DrivetrainConstants.lengthBetweenSwerveModules)) + // Configures the bumper size (dimensions of the robot bumper) + .withBumperSize(Units.Inches.of(DrivetrainConstants.lengthOfBumpers), + Units.Inches.of(DrivetrainConstants.lengthOfBumpers)); + } + + public void addGamePiece() { + SimulatedArena.getInstance().addGamePiece(new CrescendoNoteOnField(new Translation2d(3, 3))); + } + + public void clearGamePieces() { + SimulatedArena.getInstance().clearGamePieces(); + } +} diff --git a/2024-2025/main-bot/vendordeps/maple-sim.json b/2024-2025/main-bot/vendordeps/maple-sim.json new file mode 100644 index 0000000..579c260 --- /dev/null +++ b/2024-2025/main-bot/vendordeps/maple-sim.json @@ -0,0 +1,26 @@ +{ + "fileName": "maple-sim.json", + "name": "maplesim", + "version": "0.3.8", + "frcYear": "2025", + "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", + "mavenUrls": [ + "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/repos/releases", + "https://repo1.maven.org/maven2" + ], + "jsonUrl": "https://shenzhen-robotics-alliance.github.io/maple-sim/vendordep/maple-sim.json", + "javaDependencies": [ + { + "groupId": "org.ironmaple", + "artifactId": "maplesim-java", + "version": "0.3.8" + }, + { + "groupId": "org.dyn4j", + "artifactId": "dyn4j", + "version": "5.0.2" + } + ], + "jniDependencies": [], + "cppDependencies": [] +} \ No newline at end of file From f88f653e4d486dd7de8cda45e0898452bdf5cb58 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 17:15:33 -0600 Subject: [PATCH 18/32] Adjusted some variables of the Maplesim class, but normal sim works, maplesim still won't pop up tho - H --- 2024-2025/main-bot/simgui-window.json | 10 +++++----- 2024-2025/main-bot/src/main/java/frc/robot/Robot.java | 2 ++ .../java/frc/robot/subsystems/ElevatorSubsystem.java | 4 ++-- .../java/frc/robot/subsystems/MapleSimSubsystem.java | 10 +++++----- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/2024-2025/main-bot/simgui-window.json b/2024-2025/main-bot/simgui-window.json index 904d022..46e1968 100644 --- a/2024-2025/main-bot/simgui-window.json +++ b/2024-2025/main-bot/simgui-window.json @@ -6,13 +6,13 @@ "GLOBAL": { "font": "Proggy Dotted", "fps": "120", - "height": "720", + "height": "929", "maximized": "0", "style": "0", "userScale": "2", - "width": "1280", + "width": "1745", "xpos": "0", - "ypos": "23" + "ypos": "31" } }, "Table": { @@ -31,7 +31,7 @@ }, "###Joysticks": { "Collapsed": "0", - "Pos": "262,701", + "Pos": "276,429", "Size": "796,73" }, "###NetworkTables": { @@ -57,7 +57,7 @@ "###Timing": { "Collapsed": "0", "Pos": "5,150", - "Size": "135,150" + "Size": "135,173" }, "Debug##Default": { "Collapsed": "0", diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java index a4e3a30..73309d7 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java @@ -18,6 +18,7 @@ package frc.robot; import org.ironmaple.simulation.SimulatedArena; +import org.ironmaple.simulation.seasonspecific.reefscape2025.ReefscapeAlgaeOnField; import edu.wpi.first.wpilibj.TimedRobot; @@ -126,6 +127,7 @@ public void testPeriodic() { /** This function is called once when the robot is first started up. */ @Override public void simulationInit() { + SimulatedArena.getInstance(); } /** This function is called periodically whilst in simulation. */ diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index b32b47e..e7a4359 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -32,8 +32,8 @@ public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// // actually initialized m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); - m_elevatorTopLimitSwitch = new DigitalInput(1); - m_elevatorBottomLimitSwitch = new DigitalInput(2); + m_elevatorTopLimitSwitch = new DigitalInput(7); + m_elevatorBottomLimitSwitch = new DigitalInput(8); m_Encoder1 = new Encoder(0, 1); currentElevatorState = ElevatorPresets.Level1; } diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java index 60463da..dd3cd67 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java @@ -6,7 +6,7 @@ import org.ironmaple.simulation.seasonspecific.crescendo2024.CrescendoNoteOnField; import com.ctre.phoenix6.swerve.SwerveDrivetrain; -import edu.wpi.first.units.Units; +import static edu.wpi.first.units.Units.*; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.system.plant.DCMotor; @@ -37,11 +37,11 @@ public MapleSimSubsystem(CommandSwerveDrivetrain drivetrain) { COTS.WHEELS.DEFAULT_NEOPRENE_TREAD.cof, // Use the COF for Neoprene Wheels 9)) // L3 Gear ratio // Configures the track length and track width (spacing between swerve modules) - .withTrackLengthTrackWidth(Units.Inches.of(DrivetrainConstants.lengthBetweenSwerveModules), - Units.Inches.of(DrivetrainConstants.lengthBetweenSwerveModules)) + .withTrackLengthTrackWidth(Inches.of(DrivetrainConstants.lengthBetweenSwerveModules), + Inches.of(DrivetrainConstants.lengthBetweenSwerveModules)) // Configures the bumper size (dimensions of the robot bumper) - .withBumperSize(Units.Inches.of(DrivetrainConstants.lengthOfBumpers), - Units.Inches.of(DrivetrainConstants.lengthOfBumpers)); + .withBumperSize(Inches.of(DrivetrainConstants.lengthOfBumpers), + Inches.of(DrivetrainConstants.lengthOfBumpers)); } public void addGamePiece() { From 2cd580752bda0081ed5e3094721e07a1e99f9aa3 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Mon, 17 Feb 2025 17:16:15 -0600 Subject: [PATCH 19/32] Added Limelight auto aim functionality -- needs testing - H --- .../src/main/java/frc/robot/Constants.java | 5 + .../main/java/frc/robot/RobotContainer.java | 36 ++-- .../robot/subsystems/LimeLightSubsystem.java | 162 ++++++++++-------- 3 files changed, 115 insertions(+), 88 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java index c67b70a..100524e 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java @@ -4,6 +4,9 @@ package frc.robot; +import frc.robot.generated.TunerConstants; +import static edu.wpi.first.units.Units.*; + /** * 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 @@ -38,6 +41,8 @@ public static class ElevatorPreset { } public static class DrivetrainConstants { + public static final double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); + public static final double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); public static final double gearRatio = 4.59375; public static final double lengthBetweenSwerveModules = 22.5; public static final double lengthOfBumpers = 25.0; // placeholder diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 39d5b4f..e39c7e1 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -12,11 +12,10 @@ * Back + X - SysId Dynamic Reverse * Start + Y - SysId Quasistatic Forward * Start + X - SysId Quasistatic Reverse - * DPad Up - Coral Arm Up - * DPad Down - Coral Arm Down - * DPad Center - Coral Arm Stop - * Right Stick - Coral Arm Emergency Stop + * Left Joystick - Move (Field Orientated) + * Right Joystick - Turn * LB - Reset Field Centric Seed + * RT - Align with Apriltag * * Auxiliary Controller: * A - Level 1 Elevator + Coral Arm @@ -87,12 +86,11 @@ public class RobotContainer { private final SendableChooser autoChooser; /* Swerve drive platform setup */ // From Swerve Project Generator - private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // kSpeedAt12Volts desired top - // speed - private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); // 3/4 of a rotation per - // second - // max - // angular velocity + + // kSpeedAt12Volts Desired Top speed← + private double MaxSpeed = Constants.DrivetrainConstants.MaxSpeed; + // 3/4 of a rotation per second max angular velocity + private double MaxAngularRate = Constants.DrivetrainConstants.MaxAngularRate; /* Setting up bindings for necessary control of the swerve drive platform */ private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) @@ -106,7 +104,7 @@ public class RobotContainer { public final CommandSwerveDrivetrain m_DrivetrainSubsystem = TunerConstants.createDrivetrain(); // The following subsystems must be created after the drivetrain - public final LimeLightSubsystem m_LightSubsystem = new LimeLightSubsystem(m_DrivetrainSubsystem); + public final LimeLightSubsystem m_LimeLightSubsystem = new LimeLightSubsystem(m_DrivetrainSubsystem); public final MapleSimSubsystem m_MapleSimSubsystem = new MapleSimSubsystem(m_DrivetrainSubsystem); // End of Swerve Drive Platform setup @@ -115,7 +113,6 @@ public class RobotContainer { * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { - // Build an auto chooser. This will use Commands.none() as the default option. autoChooser = AutoBuilder.buildAutoChooser(); @@ -260,13 +257,13 @@ private void configureBindings(double random) { // makes coral arm go down m_auxillaryController.rightBumper() .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down)); - // stops coral arm - // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, - // CoralArmLevels.Stop)); // Emergency Stop for Coral Arm (in case it goes past the top or bottom) Note: I // don't know if the onTrue method will only run once, so test it before you use // it m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); + // stops coral arm + // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, + // CoralArmLevels.Stop)); // ====================== Drive Subsystem ====================== // // Code below is from swerve drive project generator @@ -286,6 +283,15 @@ private void configureBindings(double random) { m_driverController.b().whileTrue(m_DrivetrainSubsystem.applyRequest(() -> point.withModuleDirection( new Rotation2d(-m_driverController.getLeftY(), -m_driverController.getLeftX())))); + // limelight override + m_driverController.rightTrigger().whileTrue(m_DrivetrainSubsystem.applyRequest(() -> drive + // Drive forward with negative Y (forward) + .withVelocityX(m_LimeLightSubsystem.limelight_range_proportional()) + // Drive left with negative X (left) + .withVelocityY(-m_driverController.getLeftX() * MaxSpeed) + // Drive counterclockwise with negative X (left) + .withRotationalRate(m_LimeLightSubsystem.limelight_aim_proportional()))); + // Run SysId routines when holding back/start and X/Y. // Note that each routine should be run exactly once in a single log. m_driverController.back().and(m_driverController.y()) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java index f891237..47fe8ab 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java @@ -1,5 +1,6 @@ package frc.robot.subsystems; +import edu.wpi.first.math.MathUtil; import edu.wpi.first.math.VecBuilder; import edu.wpi.first.math.estimator.PoseEstimator; import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator; @@ -7,13 +8,13 @@ import edu.wpi.first.math.kinematics.SwerveModulePosition; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.Constants.DrivetrainConstants; import frc.robot.LimelightHelpers; // import edu.first.LimelightHelpers; // import edu.first.wpilibj. // bunch of random imports - import edu.wpi.first.math.kinematics.SwerveDriveKinematics; import edu.wpi.first.math.util.Units; @@ -22,7 +23,6 @@ import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.DriverStation.Alliance; - public class LimeLightSubsystem extends SubsystemBase { int FLIndex = 0; @@ -32,44 +32,31 @@ public class LimeLightSubsystem extends SubsystemBase { private final SwerveDrivePoseEstimator m_poseEstimator; - public LimeLightSubsystem(CommandSwerveDrivetrain swerveDriveTrain) { - m_poseEstimator = - new SwerveDrivePoseEstimator( - swerveDriveTrain.getKinematics(), - swerveDriveTrain.getPigeon2().getRotation2d(), - new SwerveModulePosition[] { - swerveDriveTrain.getModule(0).getPosition(true), - swerveDriveTrain.getModule(1).getPosition(true), - swerveDriveTrain.getModule(2).getPosition(true), - swerveDriveTrain.getModule(3).getPosition(true) - }, - new Pose2d(), - VecBuilder.fill(0.05, 0.05, Units.degreesToRadians(5)), - VecBuilder.fill(0.5, 0.5, Units.degreesToRadians(30))); + m_poseEstimator = new SwerveDrivePoseEstimator(swerveDriveTrain.getKinematics(), + swerveDriveTrain.getPigeon2().getRotation2d(), + new SwerveModulePosition[] { swerveDriveTrain.getModule(0).getPosition(true), + swerveDriveTrain.getModule(1).getPosition(true), swerveDriveTrain.getModule(2).getPosition(true), + swerveDriveTrain.getModule(3).getPosition(true) }, + new Pose2d(), VecBuilder.fill(0.05, 0.05, Units.degreesToRadians(5)), + VecBuilder.fill(0.5, 0.5, Units.degreesToRadians(30))); } + public void updateOdometry(CommandSwerveDrivetrain swerveDriveTrain) { + m_poseEstimator.update(swerveDriveTrain.getPigeon2().getRotation2d(), + new SwerveModulePosition[] { swerveDriveTrain.getModule(0).getPosition(true), + swerveDriveTrain.getModule(1).getPosition(true), swerveDriveTrain.getModule(2).getPosition(true), + swerveDriveTrain.getModule(3).getPosition(true) }); - public void updateOdometry(CommandSwerveDrivetrain swerveDriveTrain) { - - m_poseEstimator.update( - swerveDriveTrain.getPigeon2().getRotation2d(), - new SwerveModulePosition[] { - swerveDriveTrain.getModule(0).getPosition(true), - swerveDriveTrain.getModule(1).getPosition(true), - swerveDriveTrain.getModule(2).getPosition(true), - swerveDriveTrain.getModule(3).getPosition(true) - }); - - - // Is this boolean ever changing at runtime? If not, it should be a final variable - boolean useMegaTag2 = true; //set to false to use MegaTag1 + // Is this boolean ever changing at runtime? If not, it should be a final + // variable + boolean useMegaTag2 = true; // set to false to use MegaTag1 boolean doRejectUpdate = false; - if(useMegaTag2 == false) - { - // Use this line to know whether we are part of an alliance (we might not be if we are not in a match presently) + if (useMegaTag2 == false) { + // Use this line to know whether we are part of an alliance (we might not be if + // we are not in a match presently) // DriverStation.getAlliance().isEmpty() // Use this line to know whether we are on the blue alliance @@ -77,62 +64,49 @@ public void updateOdometry(CommandSwerveDrivetrain swerveDriveTrain) { // This should change depending on the alliance we are on LimelightHelpers.PoseEstimate mt1 = LimelightHelpers.getBotPoseEstimate_wpiBlue("limelight"); - - if(mt1.tagCount == 1 && mt1.rawFiducials.length == 1) - { - if(mt1.rawFiducials[0].ambiguity > .7) - { + + if (mt1.tagCount == 1 && mt1.rawFiducials.length == 1) { + if (mt1.rawFiducials[0].ambiguity > .7) { doRejectUpdate = true; } - if(mt1.rawFiducials[0].distToCamera > 3) - { + if (mt1.rawFiducials[0].distToCamera > 3) { doRejectUpdate = true; } } - if(mt1.tagCount == 0) - { + if (mt1.tagCount == 0) { doRejectUpdate = true; } - if(!doRejectUpdate) - { - m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.5,.5,9999999)); - m_poseEstimator.addVisionMeasurement( - mt1.pose, - mt1.timestampSeconds); - - swerveDriveTrain.addVisionMeasurement( - mt1.pose, - mt1.timestampSeconds, - VecBuilder.fill(.5,.5,9999999)); + if (!doRejectUpdate) { + m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.5, .5, 9999999)); + m_poseEstimator.addVisionMeasurement(mt1.pose, mt1.timestampSeconds); + + swerveDriveTrain.addVisionMeasurement(mt1.pose, mt1.timestampSeconds, VecBuilder.fill(.5, .5, 9999999)); } - } - else if (useMegaTag2 == true) - { - LimelightHelpers.SetRobotOrientation("limelight", m_poseEstimator.getEstimatedPosition().getRotation().getDegrees(), 0, 0, 0, 0, 0); - + } else if (useMegaTag2 == true) { + LimelightHelpers.SetRobotOrientation("limelight", + m_poseEstimator.getEstimatedPosition().getRotation().getDegrees(), 0, 0, 0, 0, 0); + // Same thing as above LimelightHelpers.PoseEstimate mt2 = LimelightHelpers.getBotPoseEstimate_wpiBlue_MegaTag2("limelight"); - // NOTE! The .getAngularVelocityZWorld() update is different from orignal code. The method .getRate() is depreicated and will be removed soom, so we have switched to this method. However, it is CCW+ instead of CW+, and may not return the same value - if(Math.abs(swerveDriveTrain.getPigeon2().getAngularVelocityZWorld().getValueAsDouble()) > 720) // if our angular velocity is greater than 720 degrees per second, ignore vision updates + // NOTE! The .getAngularVelocityZWorld() update is different from orignal code. + // The method .getRate() is depreicated and will be removed soom, so we have + // switched to this method. However, it is CCW+ instead of CW+, and may not + // return the same value + if (Math.abs(swerveDriveTrain.getPigeon2().getAngularVelocityZWorld().getValueAsDouble()) > 720) + // if our angular velocity is greater than 720 degrees per second, ignore vision + // updates { doRejectUpdate = true; } - if(mt2.tagCount == 0) - { + if (mt2.tagCount == 0) { doRejectUpdate = true; } - if(!doRejectUpdate) - { - m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.7,.7,9999999)); - m_poseEstimator.addVisionMeasurement( - mt2.pose, - mt2.timestampSeconds); - - swerveDriveTrain.addVisionMeasurement( - mt2.pose, - mt2.timestampSeconds, - VecBuilder.fill(.7,.7,9999999)); + if (!doRejectUpdate) { + m_poseEstimator.setVisionMeasurementStdDevs(VecBuilder.fill(.7, .7, 9999999)); + m_poseEstimator.addVisionMeasurement(mt2.pose, mt2.timestampSeconds); + + swerveDriveTrain.addVisionMeasurement(mt2.pose, mt2.timestampSeconds, VecBuilder.fill(.7, .7, 9999999)); } } } @@ -140,4 +114,46 @@ else if (useMegaTag2 == true) public PoseEstimator getPoseEstimator() { return m_poseEstimator; } + + /** + * simple proportional turning control with Limelight. "proportional control" is + * a control algorithm in which the output is proportional to the error. in this + * case, we are going to return an angular velocity that is proportional to the + * "tx" value from the Limelight. + */ + public double limelight_aim_proportional() { + // kP (constant of proportionality) + // this is a hand-tuned number that determines the aggressiveness of our + // proportional control loop + // if it is too high, the robot will oscillate around. + // if it is too low, the robot will never reach its target + // if the robot never turns in the correct direction, kP should be inverted. + double kP = .035; + + // tx ranges from (-hfov/2) to (hfov/2) in degrees. If your target is on the + // rightmost edge of your limelight 3 feed, tx should return roughly 31 degrees. + double targetingAngularVelocity = LimelightHelpers.getTX("DriveCamera") * kP; + + // convert to radians per second for our drive method + targetingAngularVelocity *= DrivetrainConstants.MaxAngularRate; + + // invert since tx is positive when the target is to the right of the crosshair + targetingAngularVelocity *= -1.0; + + return targetingAngularVelocity; + } + + /** + * simple proportional ranging control with Limelight's "ty" value this works + * best if your Limelight's mount height and target mount height are different. + * if your limelight and target are mounted at the same or similar heights, use + * "ta" (area) for target ranging rather than "ty" + */ + public double limelight_range_proportional() { + double kP = .1; + double targetingForwardSpeed = LimelightHelpers.getTY("limelight") * kP; + targetingForwardSpeed *= DrivetrainConstants.MaxSpeed; + targetingForwardSpeed *= -1.0; + return targetingForwardSpeed; + } } From 28221aac9d100af51c368267024fbe92984909a5 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 16:08:38 -0600 Subject: [PATCH 20/32] robot changed - me --- 2024-2025/main-bot/src/main/java/frc/robot/Robot.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java index 73309d7..9a5e699 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Robot.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Robot.java @@ -20,6 +20,8 @@ import org.ironmaple.simulation.SimulatedArena; import org.ironmaple.simulation.seasonspecific.reefscape2025.ReefscapeAlgaeOnField; +import com.ctre.phoenix6.SignalLogger; + import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj2.command.Command; @@ -72,6 +74,7 @@ public void robotPeriodic() { /** This function is called once each time the robot enters Disabled mode. */ @Override public void disabledInit() { + SignalLogger.stop(); } @Override @@ -106,6 +109,8 @@ public void teleopInit() { if (m_autonomousCommand != null) { m_autonomousCommand.cancel(); } + SignalLogger.setPath("C:\\Users\\teams\\Desktop\\2025HootLogs"); + SignalLogger.start(); } /** This function is called periodically during operator control. */ From 7d6c2eccf603e61e64552cdb5231c6f9b96daf04 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:32:52 -0600 Subject: [PATCH 21/32] Sim GUI adjustments and Small Pathplanner example --- 2024-2025/main-bot/simgui-window.json | 38 ++++++++----- 2024-2025/main-bot/simgui.json | 43 +++++++++------ .../deploy/pathplanner/paths/New Path.path | 54 +++++++++++++++++++ 3 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 2024-2025/main-bot/src/main/deploy/pathplanner/paths/New Path.path diff --git a/2024-2025/main-bot/simgui-window.json b/2024-2025/main-bot/simgui-window.json index 46e1968..42b95bd 100644 --- a/2024-2025/main-bot/simgui-window.json +++ b/2024-2025/main-bot/simgui-window.json @@ -6,13 +6,13 @@ "GLOBAL": { "font": "Proggy Dotted", "fps": "120", - "height": "929", - "maximized": "0", + "height": "1057", + "maximized": "1", "style": "0", "userScale": "2", - "width": "1745", - "xpos": "0", - "ypos": "31" + "width": "1920", + "xpos": "1920", + "ypos": "23" } }, "Table": { @@ -26,29 +26,39 @@ "Window": { "###FMS": { "Collapsed": "0", - "Pos": "17,595", - "Size": "283,140" + "Pos": "10,346", + "Size": "169,184" }, "###Joysticks": { "Collapsed": "0", - "Pos": "276,429", - "Size": "796,73" + "Pos": "149,27", + "Size": "796,234" }, "###NetworkTables": { "Collapsed": "1", - "Pos": "250,277", + "Pos": "28,566", "Size": "750,440" }, "###NetworkTables Info": { - "Collapsed": "0", - "Pos": "250,130", + "Collapsed": "1", + "Pos": "26,593", "Size": "750,145" }, "###Other Devices": { "Collapsed": "0", - "Pos": "1261,42", + "Pos": "1651,30", "Size": "250,695" }, + "###Plot <0>": { + "Collapsed": "0", + "Pos": "948,27", + "Size": "700,400" + }, + "###Plot <1>": { + "Collapsed": "0", + "Pos": "949,433", + "Size": "700,400" + }, "###System Joysticks": { "Collapsed": "0", "Pos": "12,348", @@ -57,7 +67,7 @@ "###Timing": { "Collapsed": "0", "Pos": "5,150", - "Size": "135,173" + "Size": "135,150" }, "Debug##Default": { "Collapsed": "0", diff --git a/2024-2025/main-bot/simgui.json b/2024-2025/main-bot/simgui.json index 322d8a3..d368b5e 100644 --- a/2024-2025/main-bot/simgui.json +++ b/2024-2025/main-bot/simgui.json @@ -1,13 +1,4 @@ { - "HALProvider": { - "Other Devices": { - "Talon FX (v6)[13]": { - "header": { - "open": true - } - } - } - }, "NTProvider": { "types": { "/FMSInfo": "FMSInfo", @@ -21,9 +12,20 @@ } }, "NetworkTables": { + "Persistent Values": { + "open": false + }, + "Retained Values": { + "open": false + }, + "Transitory Values": { + "open": false + }, "transitory": { "DriveState": { - "open": true + "ChassisSpeeds##v_/DriveState/Speeds": { + "open": true + } }, "SmartDashboard": { "Alerts": { @@ -43,15 +45,26 @@ }, "Module 3": { "open": true - }, - "open": true + } } } }, "NetworkTables Info": { - "Clients": { - "open": true - }, "visible": true + }, + "Plot": { + "Plot <0>": { + "plots": [ + { + "backgroundColor": [ + 0.0, + 0.0, + 0.0, + 0.8500000238418579 + ], + "height": 338 + } + ] + } } } diff --git a/2024-2025/main-bot/src/main/deploy/pathplanner/paths/New Path.path b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/New Path.path new file mode 100644 index 0000000..8ea16a6 --- /dev/null +++ b/2024-2025/main-bot/src/main/deploy/pathplanner/paths/New Path.path @@ -0,0 +1,54 @@ +{ + "version": "2025.0", + "waypoints": [ + { + "anchor": { + "x": 2.0, + "y": 7.0 + }, + "prevControl": null, + "nextControl": { + "x": 3.0, + "y": 7.0 + }, + "isLocked": false, + "linkedName": null + }, + { + "anchor": { + "x": 3.4408818493150686, + "y": 4.009974315068493 + }, + "prevControl": { + "x": 2.4408818493150686, + "y": 4.009974315068493 + }, + "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 From b22fccc101a472553cc42c43bd8aef801e8bfe87 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:33:39 -0600 Subject: [PATCH 22/32] Change some swerve drive variables to constants --- .../src/main/java/frc/robot/Constants.java | 71 ++- .../frc/robot/generated/TunerConstants.java | 453 ++++++++---------- .../subsystems/CommandSwerveDrivetrain.java | 237 +++++---- 3 files changed, 362 insertions(+), 399 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java index 100524e..d5f6a4f 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java @@ -6,6 +6,9 @@ import frc.robot.generated.TunerConstants; import static edu.wpi.first.units.Units.*; +import edu.wpi.first.units.measure.Angle; + +import edu.wpi.first.units.measure.Distance; /** * The Constants class provides a convenient place for teams to hold robot-wide @@ -40,7 +43,25 @@ public static class ElevatorPreset { } } - public static class DrivetrainConstants { + public static final class AlgaeArmConstants { + public static final int algaeArmBarID = 8;// placeholder + } + + public static class CoralArmConstants { + public static final int coralArmID = 69;// placeholder with funny number hehe + public static final int kCoralEncoderTopValue = 1000; + public static final int kCoralEncoderTopBuffer = kCoralEncoderTopValue + 10; + public static final int kCoralEncoderBottomValue = 0; + public static final int kCoralEncoderBottomBuffer = kCoralEncoderBottomValue - 10; + } + + public static class ClimberConstants { + public static final int kLeftClimberMotorID = 25; // TEMP + public static final int kRightClimberMotorID = 26; // TEMP + public static final int kClimberLimitSwitchID = 6; // TEMP + } + + public static class DriveTrainConstants { public static final double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); public static final double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); public static final double gearRatio = 4.59375; @@ -53,17 +74,41 @@ public static class DrivetrainConstants { public static final int FLTurnMotorID = 1; // You can finish this out Scott! :D - } - - public static final class AlgaeArmConstants { - public static final int algaeArmBarID = 8;// placeholder - } - - public static class CoralArmConstants { - public static final int coralArmID = 69;// placeholder with funny number hehe - public static final int kCoralEncoderTopValue = 1000; - public static final int kCoralEncoderTopBuffer = kCoralEncoderTopValue + 10; - public static final int kCoralEncoderBottomValue = 0; - public static final int kCoralEncoderBottomBuffer = kCoralEncoderBottomValue - 10; + // Front Left Constants + public static final int kFrontLeftDriveMotorId = 42; + public static final int kFrontLeftSteerMotorId = 1; + public static final int kFrontLeftEncoderId = 0; + public static final Angle kFrontLeftEncoderOffset = Rotations.of(-0.432373046875); + public static final boolean kFrontLeftSteerMotorInverted = true; + public static final boolean kFrontLeftEncoderInverted = false; + public static final Distance kFrontLeftXPos = Inches.of(11.25); + public static final Distance kFrontLeftYPos = Inches.of(11.25); + // Front Right Constants + public static final int kFrontRightDriveMotorId = 2; + public static final int kFrontRightSteerMotorId = 3; + public static final int kFrontRightEncoderId = 2; + public static final Angle kFrontRightEncoderOffset = Rotations.of(-0.282958984375); + public static final boolean kFrontRightSteerMotorInverted = true; + public static final boolean kFrontRightEncoderInverted = false; + public static final Distance kFrontRightXPos = Inches.of(11.25); + public static final Distance kFrontRightYPos = Inches.of(-11.25); + // Back Left Constants + public static final int kBackLeftDriveMotorId = 6; + public static final int kBackLeftSteerMotorId = 7; + public static final int kBackLeftEncoderId = 6; + public static final Angle kBackLeftEncoderOffset = Rotations.of(-0.09326171875); + public static final boolean kBackLeftSteerMotorInverted = true; + public static final boolean kBackLeftEncoderInverted = false; + public static final Distance kBackLeftXPos = Inches.of(-11.25); + public static final Distance kBackLeftYPos = Inches.of(11.25); + // Back Right Constants + public static final int kBackRightDriveMotorId = 4; + public static final int kBackRightSteerMotorId = 0; + public static final int kBackRightEncoderId = 4; + public static final Angle kBackRightEncoderOffset = Rotations.of(-0.111328125); + public static final boolean kBackRightSteerMotorInverted = true; + public static final boolean kBackRightEncoderInverted = false; + public static final Distance kBackRightXPos = Inches.of(-11.25); + public static final Distance kBackRightYPos = Inches.of(-11.25); } } diff --git a/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java b/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java index eb5d481..bcf08ff 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/generated/TunerConstants.java @@ -13,274 +13,213 @@ import edu.wpi.first.math.numbers.N1; import edu.wpi.first.math.numbers.N3; import edu.wpi.first.units.measure.*; - +import frc.robot.Constants; import frc.robot.subsystems.CommandSwerveDrivetrain; // Generated by the Tuner X Swerve Project Generator // https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html public class TunerConstants { - // Both sets of gains need to be tuned to your individual robot. - - // The steer motor uses any SwerveModule.SteerRequestType control request with the - // output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput - private static final Slot0Configs steerGains = new Slot0Configs() - .withKP(100).withKI(0).withKD(0.5) - .withKS(0.1).withKV(1.66).withKA(0) - .withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign); - // When using closed-loop control, the drive motor uses the control - // output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput - private static final Slot0Configs driveGains = new Slot0Configs() - .withKP(0.1).withKI(0).withKD(0) - .withKS(0).withKV(0.124); - - // The closed-loop output type to use for the steer motors; - // This affects the PID/FF gains for the steer motors - private static final ClosedLoopOutputType kSteerClosedLoopOutput = ClosedLoopOutputType.Voltage; - // The closed-loop output type to use for the drive motors; - // This affects the PID/FF gains for the drive motors - private static final ClosedLoopOutputType kDriveClosedLoopOutput = ClosedLoopOutputType.Voltage; - - // The type of motor used for the drive motor - private static final DriveMotorArrangement kDriveMotorType = DriveMotorArrangement.TalonFX_Integrated; - // The type of motor used for the drive motor - private static final SteerMotorArrangement kSteerMotorType = SteerMotorArrangement.TalonFX_Integrated; - - // The remote sensor feedback type to use for the steer motors; - // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote* - private static final SteerFeedbackType kSteerFeedbackType = SteerFeedbackType.FusedCANcoder; - - // The stator current at which the wheels start to slip; - // This needs to be tuned to your individual robot - private static final Current kSlipCurrent = Amps.of(120.0); - - // Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null. - // Some configs will be overwritten; check the `with*InitialConfigs()` API documentation. - private static final TalonFXConfiguration driveInitialConfigs = new TalonFXConfiguration(); - private static final TalonFXConfiguration steerInitialConfigs = new TalonFXConfiguration() - .withCurrentLimits( - new CurrentLimitsConfigs() - // Swerve azimuth does not require much torque output, so we can set a relatively low - // stator current limit to help avoid brownouts without impacting performance. - .withStatorCurrentLimit(Amps.of(60)) - .withStatorCurrentLimitEnable(true) - ); - private static final CANcoderConfiguration encoderInitialConfigs = new CANcoderConfiguration(); - // Configs for the Pigeon 2; leave this null to skip applying Pigeon 2 configs - private static final Pigeon2Configuration pigeonConfigs = null; - - // CAN bus that the devices are located on; - // All swerve devices must share the same CAN bus - public static final CANBus kCANBus = new CANBus("", "./logs/example.hoot"); - - // Theoretical free speed (m/s) at 12 V applied output; - // This needs to be tuned to your individual robot - public static final LinearVelocity kSpeedAt12Volts = MetersPerSecond.of(6.95); - - // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns; - // This may need to be tuned to your individual robot - private static final double kCoupleRatio = 3.5; - - private static final double kDriveGearRatio = 4.59375; - private static final double kSteerGearRatio = 13.371428571428572; - private static final Distance kWheelRadius = Inches.of(2); - - private static final boolean kInvertLeftSide = false; - private static final boolean kInvertRightSide = true; - - private static final int kPigeonId = 20; - - // These are only used for simulation - private static final MomentOfInertia kSteerInertia = KilogramSquareMeters.of(0.01); - private static final MomentOfInertia kDriveInertia = KilogramSquareMeters.of(0.01); - // Simulated voltage necessary to overcome friction - private static final Voltage kSteerFrictionVoltage = Volts.of(0.2); - private static final Voltage kDriveFrictionVoltage = Volts.of(0.2); - - public static final SwerveDrivetrainConstants DrivetrainConstants = new SwerveDrivetrainConstants() - .withCANBusName(kCANBus.getName()) - .withPigeon2Id(kPigeonId) - .withPigeon2Configs(pigeonConfigs); - - private static final SwerveModuleConstantsFactory ConstantCreator = - new SwerveModuleConstantsFactory() - .withDriveMotorGearRatio(kDriveGearRatio) - .withSteerMotorGearRatio(kSteerGearRatio) - .withCouplingGearRatio(kCoupleRatio) - .withWheelRadius(kWheelRadius) - .withSteerMotorGains(steerGains) - .withDriveMotorGains(driveGains) - .withSteerMotorClosedLoopOutput(kSteerClosedLoopOutput) - .withDriveMotorClosedLoopOutput(kDriveClosedLoopOutput) - .withSlipCurrent(kSlipCurrent) - .withSpeedAt12Volts(kSpeedAt12Volts) - .withDriveMotorType(kDriveMotorType) - .withSteerMotorType(kSteerMotorType) - .withFeedbackSource(kSteerFeedbackType) - .withDriveMotorInitialConfigs(driveInitialConfigs) - .withSteerMotorInitialConfigs(steerInitialConfigs) - .withEncoderInitialConfigs(encoderInitialConfigs) - .withSteerInertia(kSteerInertia) - .withDriveInertia(kDriveInertia) - .withSteerFrictionVoltage(kSteerFrictionVoltage) - .withDriveFrictionVoltage(kDriveFrictionVoltage); - - - // Front Left - private static final int kFrontLeftDriveMotorId = 42; - private static final int kFrontLeftSteerMotorId = 1; - private static final int kFrontLeftEncoderId = 0; - private static final Angle kFrontLeftEncoderOffset = Rotations.of(-0.432373046875); - private static final boolean kFrontLeftSteerMotorInverted = true; - private static final boolean kFrontLeftEncoderInverted = false; - - private static final Distance kFrontLeftXPos = Inches.of(11.25); - private static final Distance kFrontLeftYPos = Inches.of(11.25); - - // Front Right - private static final int kFrontRightDriveMotorId = 2; - private static final int kFrontRightSteerMotorId = 3; - private static final int kFrontRightEncoderId = 2; - private static final Angle kFrontRightEncoderOffset = Rotations.of(-0.282958984375); - private static final boolean kFrontRightSteerMotorInverted = true; - private static final boolean kFrontRightEncoderInverted = false; - - private static final Distance kFrontRightXPos = Inches.of(11.25); - private static final Distance kFrontRightYPos = Inches.of(-11.25); - - // Back Left - private static final int kBackLeftDriveMotorId = 6; - private static final int kBackLeftSteerMotorId = 7; - private static final int kBackLeftEncoderId = 6; - private static final Angle kBackLeftEncoderOffset = Rotations.of(-0.09326171875); - private static final boolean kBackLeftSteerMotorInverted = true; - private static final boolean kBackLeftEncoderInverted = false; - - private static final Distance kBackLeftXPos = Inches.of(-11.25); - private static final Distance kBackLeftYPos = Inches.of(11.25); - - // Back Right - private static final int kBackRightDriveMotorId = 4; - private static final int kBackRightSteerMotorId = 0; - private static final int kBackRightEncoderId = 4; - private static final Angle kBackRightEncoderOffset = Rotations.of(-0.111328125); - private static final boolean kBackRightSteerMotorInverted = true; - private static final boolean kBackRightEncoderInverted = false; - - private static final Distance kBackRightXPos = Inches.of(-11.25); - private static final Distance kBackRightYPos = Inches.of(-11.25); - - - public static final SwerveModuleConstants FrontLeft = - ConstantCreator.createModuleConstants( - kFrontLeftSteerMotorId, kFrontLeftDriveMotorId, kFrontLeftEncoderId, kFrontLeftEncoderOffset, - kFrontLeftXPos, kFrontLeftYPos, kInvertLeftSide, kFrontLeftSteerMotorInverted, kFrontLeftEncoderInverted - ); - public static final SwerveModuleConstants FrontRight = - ConstantCreator.createModuleConstants( - kFrontRightSteerMotorId, kFrontRightDriveMotorId, kFrontRightEncoderId, kFrontRightEncoderOffset, - kFrontRightXPos, kFrontRightYPos, kInvertRightSide, kFrontRightSteerMotorInverted, kFrontRightEncoderInverted - ); - public static final SwerveModuleConstants BackLeft = - ConstantCreator.createModuleConstants( - kBackLeftSteerMotorId, kBackLeftDriveMotorId, kBackLeftEncoderId, kBackLeftEncoderOffset, - kBackLeftXPos, kBackLeftYPos, kInvertLeftSide, kBackLeftSteerMotorInverted, kBackLeftEncoderInverted - ); - public static final SwerveModuleConstants BackRight = - ConstantCreator.createModuleConstants( - kBackRightSteerMotorId, kBackRightDriveMotorId, kBackRightEncoderId, kBackRightEncoderOffset, - kBackRightXPos, kBackRightYPos, kInvertRightSide, kBackRightSteerMotorInverted, kBackRightEncoderInverted - ); - - /** - * Creates a CommandSwerveDrivetrain instance. - * This should only be called once in your robot program,. - */ - public static CommandSwerveDrivetrain createDrivetrain() { - return new CommandSwerveDrivetrain( - DrivetrainConstants, FrontLeft, FrontRight, BackLeft, BackRight - ); - } - - - /** - * Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected device types. - */ - public static class TunerSwerveDrivetrain extends SwerveDrivetrain { - /** - * Constructs a CTRE SwerveDrivetrain using the specified constants. - *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. - * - * @param drivetrainConstants Drivetrain-wide constants for the swerve drive - * @param modules Constants for each specific module - */ - public TunerSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - SwerveModuleConstants... modules - ) { - super( - TalonFX::new, TalonFX::new, CANcoder::new, - drivetrainConstants, modules - ); - } + // Both sets of gains need to be tuned to your individual robot. + + // The steer motor uses any SwerveModule.SteerRequestType control request with + // the + // output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput + private static final Slot0Configs steerGains = new Slot0Configs().withKP(100).withKI(0).withKD(0.5).withKS(0.1) + .withKV(1.66).withKA(0).withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign); + // When using closed-loop control, the drive motor uses the control + // output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput + private static final Slot0Configs driveGains = new Slot0Configs().withKP(0.1).withKI(0).withKD(0).withKS(0) + .withKV(0.124); + + // The closed-loop output type to use for the steer motors; + // This affects the PID/FF gains for the steer motors + private static final ClosedLoopOutputType kSteerClosedLoopOutput = ClosedLoopOutputType.Voltage; + // The closed-loop output type to use for the drive motors; + // This affects the PID/FF gains for the drive motors + private static final ClosedLoopOutputType kDriveClosedLoopOutput = ClosedLoopOutputType.Voltage; + + // The type of motor used for the drive motor + private static final DriveMotorArrangement kDriveMotorType = DriveMotorArrangement.TalonFX_Integrated; + // The type of motor used for the drive motor + private static final SteerMotorArrangement kSteerMotorType = SteerMotorArrangement.TalonFX_Integrated; + + // The remote sensor feedback type to use for the steer motors; + // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote* + private static final SteerFeedbackType kSteerFeedbackType = SteerFeedbackType.FusedCANcoder; + + // The stator current at which the wheels start to slip; + // This needs to be tuned to your individual robot + private static final Current kSlipCurrent = Amps.of(120.0); + + // Initial configs for the drive and steer motors and the azimuth encoder; these + // cannot be null. + // Some configs will be overwritten; check the `with*InitialConfigs()` API + // documentation. + private static final TalonFXConfiguration driveInitialConfigs = new TalonFXConfiguration(); + private static final TalonFXConfiguration steerInitialConfigs = new TalonFXConfiguration() + .withCurrentLimits(new CurrentLimitsConfigs() + // Swerve azimuth does not require much torque output, so we can set a + // relatively low + // stator current limit to help avoid brownouts without impacting performance. + .withStatorCurrentLimit(Amps.of(60)).withStatorCurrentLimitEnable(true)); + private static final CANcoderConfiguration encoderInitialConfigs = new CANcoderConfiguration(); + // Configs for the Pigeon 2; leave this null to skip applying Pigeon 2 configs + private static final Pigeon2Configuration pigeonConfigs = null; + + // CAN bus that the devices are located on; + // All swerve devices must share the same CAN bus + public static final CANBus kCANBus = new CANBus("", "./logs/example.hoot"); + + // Theoretical free speed (m/s) at 12 V applied output; + // This needs to be tuned to your individual robot + public static final LinearVelocity kSpeedAt12Volts = MetersPerSecond.of(6.95); + + // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns; + // This may need to be tuned to your individual robot + private static final double kCoupleRatio = 3.5; + + private static final double kDriveGearRatio = 4.59375; + private static final double kSteerGearRatio = 13.371428571428572; + private static final Distance kWheelRadius = Inches.of(2); + + private static final boolean kInvertLeftSide = false; + private static final boolean kInvertRightSide = true; + + private static final int kPigeonId = 20; + + // These are only used for simulation + private static final MomentOfInertia kSteerInertia = KilogramSquareMeters.of(0.01); + private static final MomentOfInertia kDriveInertia = KilogramSquareMeters.of(0.01); + // Simulated voltage necessary to overcome friction + private static final Voltage kSteerFrictionVoltage = Volts.of(0.2); + private static final Voltage kDriveFrictionVoltage = Volts.of(0.2); + + public static final SwerveDrivetrainConstants DrivetrainConstants = new SwerveDrivetrainConstants() + .withCANBusName(kCANBus.getName()).withPigeon2Id(kPigeonId).withPigeon2Configs(pigeonConfigs); + + private static final SwerveModuleConstantsFactory ConstantCreator = new SwerveModuleConstantsFactory() + .withDriveMotorGearRatio(kDriveGearRatio).withSteerMotorGearRatio(kSteerGearRatio) + .withCouplingGearRatio(kCoupleRatio).withWheelRadius(kWheelRadius) + .withSteerMotorGains(steerGains).withDriveMotorGains(driveGains) + .withSteerMotorClosedLoopOutput(kSteerClosedLoopOutput) + .withDriveMotorClosedLoopOutput(kDriveClosedLoopOutput).withSlipCurrent(kSlipCurrent) + .withSpeedAt12Volts(kSpeedAt12Volts).withDriveMotorType(kDriveMotorType) + .withSteerMotorType(kSteerMotorType).withFeedbackSource(kSteerFeedbackType) + .withDriveMotorInitialConfigs(driveInitialConfigs) + .withSteerMotorInitialConfigs(steerInitialConfigs) + .withEncoderInitialConfigs(encoderInitialConfigs).withSteerInertia(kSteerInertia) + .withDriveInertia(kDriveInertia).withSteerFrictionVoltage(kSteerFrictionVoltage) + .withDriveFrictionVoltage(kDriveFrictionVoltage); + + public static final SwerveModuleConstants FrontLeft = ConstantCreator + .createModuleConstants(Constants.DriveTrainConstants.kFrontLeftSteerMotorId, + Constants.DriveTrainConstants.kFrontLeftDriveMotorId, + Constants.DriveTrainConstants.kFrontLeftEncoderId, + Constants.DriveTrainConstants.kFrontLeftEncoderOffset, + Constants.DriveTrainConstants.kFrontLeftXPos, + Constants.DriveTrainConstants.kFrontLeftYPos, kInvertLeftSide, + Constants.DriveTrainConstants.kFrontLeftSteerMotorInverted, + Constants.DriveTrainConstants.kFrontLeftEncoderInverted); + public static final SwerveModuleConstants FrontRight = ConstantCreator + .createModuleConstants(Constants.DriveTrainConstants.kFrontRightSteerMotorId, + Constants.DriveTrainConstants.kFrontRightDriveMotorId, + Constants.DriveTrainConstants.kFrontRightEncoderId, + Constants.DriveTrainConstants.kFrontRightEncoderOffset, + Constants.DriveTrainConstants.kFrontRightXPos, + Constants.DriveTrainConstants.kFrontRightYPos, kInvertRightSide, + Constants.DriveTrainConstants.kFrontRightSteerMotorInverted, + Constants.DriveTrainConstants.kFrontRightEncoderInverted); + public static final SwerveModuleConstants BackLeft = ConstantCreator + .createModuleConstants(Constants.DriveTrainConstants.kBackLeftSteerMotorId, + Constants.DriveTrainConstants.kBackLeftDriveMotorId, + Constants.DriveTrainConstants.kBackLeftEncoderId, + Constants.DriveTrainConstants.kBackLeftEncoderOffset, + Constants.DriveTrainConstants.kBackLeftXPos, + Constants.DriveTrainConstants.kBackLeftYPos, kInvertLeftSide, + Constants.DriveTrainConstants.kBackLeftSteerMotorInverted, + Constants.DriveTrainConstants.kBackLeftEncoderInverted); + public static final SwerveModuleConstants BackRight = ConstantCreator + .createModuleConstants(Constants.DriveTrainConstants.kBackRightSteerMotorId, + Constants.DriveTrainConstants.kBackRightDriveMotorId, + Constants.DriveTrainConstants.kBackRightEncoderId, + Constants.DriveTrainConstants.kBackRightEncoderOffset, + Constants.DriveTrainConstants.kBackRightXPos, + Constants.DriveTrainConstants.kBackRightYPos, kInvertRightSide, + Constants.DriveTrainConstants.kBackRightSteerMotorInverted, + Constants.DriveTrainConstants.kBackRightEncoderInverted); /** - * Constructs a CTRE SwerveDrivetrain using the specified constants. - *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. - * - * @param drivetrainConstants Drivetrain-wide constants for the swerve drive - * @param odometryUpdateFrequency The frequency to run the odometry loop. If - * unspecified or set to 0 Hz, this is 250 Hz on - * CAN FD, and 100 Hz on CAN 2.0. - * @param modules Constants for each specific module + * Creates a CommandSwerveDrivetrain instance. This should only be called once + * in your robot program,. */ - public TunerSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - double odometryUpdateFrequency, - SwerveModuleConstants... modules - ) { - super( - TalonFX::new, TalonFX::new, CANcoder::new, - drivetrainConstants, odometryUpdateFrequency, modules - ); + public static CommandSwerveDrivetrain createDrivetrain() { + return new CommandSwerveDrivetrain(DrivetrainConstants, FrontLeft, FrontRight, BackLeft, BackRight); } /** - * Constructs a CTRE SwerveDrivetrain using the specified constants. - *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. - * - * @param drivetrainConstants Drivetrain-wide constants for the swerve drive - * @param odometryUpdateFrequency The frequency to run the odometry loop. If - * unspecified or set to 0 Hz, this is 250 Hz on - * CAN FD, and 100 Hz on CAN 2.0. - * @param odometryStandardDeviation The standard deviation for odometry calculation - * in the form [x, y, theta]ᵀ, with units in meters - * and radians - * @param visionStandardDeviation The standard deviation for vision calculation - * in the form [x, y, theta]ᵀ, with units in meters - * and radians - * @param modules Constants for each specific module + * Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected + * device types. */ - public TunerSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - double odometryUpdateFrequency, - Matrix odometryStandardDeviation, - Matrix visionStandardDeviation, - SwerveModuleConstants... modules - ) { - super( - TalonFX::new, TalonFX::new, CANcoder::new, - drivetrainConstants, odometryUpdateFrequency, - odometryStandardDeviation, visionStandardDeviation, modules - ); + public static class TunerSwerveDrivetrain extends SwerveDrivetrain { + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, + SwerveModuleConstants... modules) { + super(TalonFX::new, TalonFX::new, CANcoder::new, drivetrainConstants, modules); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz on + * CAN FD, and 100 Hz on CAN 2.0. + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, SwerveModuleConstants... modules) { + super(TalonFX::new, TalonFX::new, CANcoder::new, drivetrainConstants, odometryUpdateFrequency, + modules); + } + + /** + * Constructs a CTRE SwerveDrivetrain using the specified constants. + *

+ * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. + * + * @param drivetrainConstants Drivetrain-wide constants for the swerve + * drive + * @param odometryUpdateFrequency The frequency to run the odometry loop. If + * unspecified or set to 0 Hz, this is 250 Hz + * on CAN FD, and 100 Hz on CAN 2.0. + * @param odometryStandardDeviation The standard deviation for odometry + * calculation in the form [x, y, theta]ᵀ, with + * units in meters and radians + * @param visionStandardDeviation The standard deviation for vision + * calculation in the form [x, y, theta]ᵀ, with + * units in meters and radians + * @param modules Constants for each specific module + */ + public TunerSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, + double odometryUpdateFrequency, Matrix odometryStandardDeviation, + Matrix visionStandardDeviation, SwerveModuleConstants... modules) { + super(TalonFX::new, TalonFX::new, CANcoder::new, drivetrainConstants, odometryUpdateFrequency, + odometryStandardDeviation, visionStandardDeviation, modules); + } } - } } diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java index 3f70c28..3566d94 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java @@ -53,64 +53,50 @@ public class CommandSwerveDrivetrain extends TunerSwerveDrivetrain implements Su private final SwerveRequest.SysIdSwerveSteerGains m_steerCharacterization = new SwerveRequest.SysIdSwerveSteerGains(); private final SwerveRequest.SysIdSwerveRotation m_rotationCharacterization = new SwerveRequest.SysIdSwerveRotation(); - /* SysId routine for characterizing translation. This is used to find PID gains for the drive motors. */ - private final SysIdRoutine m_sysIdRoutineTranslation = new SysIdRoutine( - new SysIdRoutine.Config( - null, // Use default ramp rate (1 V/s) + /* + * SysId routine for characterizing translation. This is used to find PID gains + * for the drive motors. + */ + private final SysIdRoutine m_sysIdRoutineTranslation = new SysIdRoutine(new SysIdRoutine.Config(null, // Use default + // ramp rate + // (1 V/s) Volts.of(4), // Reduce dynamic step voltage to 4 V to prevent brownout - null, // Use default timeout (10 s) + null, // Use default timeout (10 s) // Log state with SignalLogger class - state -> SignalLogger.writeString("SysIdTranslation_State", state.toString()) - ), - new SysIdRoutine.Mechanism( - output -> setControl(m_translationCharacterization.withVolts(output)), - null, - this - ) - ); + state -> SignalLogger.writeString("SysIdTranslation_State", state.toString())), + new SysIdRoutine.Mechanism(output -> setControl(m_translationCharacterization.withVolts(output)), null, + this)); - /* SysId routine for characterizing steer. This is used to find PID gains for the steer motors. */ - private final SysIdRoutine m_sysIdRoutineSteer = new SysIdRoutine( - new SysIdRoutine.Config( - null, // Use default ramp rate (1 V/s) + /* + * SysId routine for characterizing steer. This is used to find PID gains for + * the steer motors. + */ + private final SysIdRoutine m_sysIdRoutineSteer = new SysIdRoutine(new SysIdRoutine.Config(null, // Use default ramp + // rate (1 V/s) Volts.of(7), // Use dynamic voltage of 7 V - null, // Use default timeout (10 s) + null, // Use default timeout (10 s) // Log state with SignalLogger class - state -> SignalLogger.writeString("SysIdSteer_State", state.toString()) - ), - new SysIdRoutine.Mechanism( - volts -> setControl(m_steerCharacterization.withVolts(volts)), - null, - this - ) - ); + state -> SignalLogger.writeString("SysIdSteer_State", state.toString())), + new SysIdRoutine.Mechanism(volts -> setControl(m_steerCharacterization.withVolts(volts)), null, this)); /* - * SysId routine for characterizing rotation. - * This is used to find PID gains for the FieldCentricFacingAngle HeadingController. - * See the documentation of SwerveRequest.SysIdSwerveRotation for info on importing the log to SysId. + * SysId routine for characterizing rotation. This is used to find PID gains for + * the FieldCentricFacingAngle HeadingController. See the documentation of + * SwerveRequest.SysIdSwerveRotation for info on importing the log to SysId. */ - private final SysIdRoutine m_sysIdRoutineRotation = new SysIdRoutine( - new SysIdRoutine.Config( + private final SysIdRoutine m_sysIdRoutineRotation = new SysIdRoutine(new SysIdRoutine.Config( /* This is in radians per second², but SysId only supports "volts per second" */ Volts.of(Math.PI / 6).per(Second), /* This is in radians per second, but SysId only supports "volts" */ - Volts.of(Math.PI), - null, // Use default timeout (10 s) + Volts.of(Math.PI), null, // Use default timeout (10 s) // Log state with SignalLogger class - state -> SignalLogger.writeString("SysIdRotation_State", state.toString()) - ), - new SysIdRoutine.Mechanism( - output -> { + state -> SignalLogger.writeString("SysIdRotation_State", state.toString())), + new SysIdRoutine.Mechanism(output -> { /* output is actually radians per second, but SysId only supports "volts" */ setControl(m_rotationCharacterization.withRotationalRate(output.in(Volts))); /* also log the requested output for SysId */ SignalLogger.writeDouble("Rotational_Rate", output.in(Volts)); - }, - null, - this - ) - ); + }, null, this)); /* The SysId routine to test */ private SysIdRoutine m_sysIdRoutineToApply = m_sysIdRoutineTranslation; @@ -118,17 +104,15 @@ public class CommandSwerveDrivetrain extends TunerSwerveDrivetrain implements Su /** * Constructs a CTRE SwerveDrivetrain using the specified constants. *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. + * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. * - * @param drivetrainConstants Drivetrain-wide constants for the swerve drive - * @param modules Constants for each specific module + * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param modules Constants for each specific module */ - public CommandSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - SwerveModuleConstants... modules - ) { + public CommandSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, + SwerveModuleConstants... modules) { super(drivetrainConstants, modules); if (Utils.isSimulation()) { startSimThread(); @@ -139,9 +123,9 @@ public CommandSwerveDrivetrain( /** * Constructs a CTRE SwerveDrivetrain using the specified constants. *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. + * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. * * @param drivetrainConstants Drivetrain-wide constants for the swerve drive * @param odometryUpdateFrequency The frequency to run the odometry loop. If @@ -149,11 +133,8 @@ public CommandSwerveDrivetrain( * CAN FD, and 100 Hz on CAN 2.0. * @param modules Constants for each specific module */ - public CommandSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - double odometryUpdateFrequency, - SwerveModuleConstants... modules - ) { + public CommandSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, double odometryUpdateFrequency, + SwerveModuleConstants... modules) { super(drivetrainConstants, odometryUpdateFrequency, modules); if (Utils.isSimulation()) { startSimThread(); @@ -164,30 +145,28 @@ public CommandSwerveDrivetrain( /** * Constructs a CTRE SwerveDrivetrain using the specified constants. *

- * This constructs the underlying hardware devices, so users should not construct - * the devices themselves. If they need the devices, they can access them through - * getters in the classes. + * This constructs the underlying hardware devices, so users should not + * construct the devices themselves. If they need the devices, they can access + * them through getters in the classes. * - * @param drivetrainConstants Drivetrain-wide constants for the swerve drive + * @param drivetrainConstants Drivetrain-wide constants for the swerve + * drive * @param odometryUpdateFrequency The frequency to run the odometry loop. If - * unspecified or set to 0 Hz, this is 250 Hz on - * CAN FD, and 100 Hz on CAN 2.0. - * @param odometryStandardDeviation The standard deviation for odometry calculation - * in the form [x, y, theta]ᵀ, with units in meters - * and radians - * @param visionStandardDeviation The standard deviation for vision calculation - * in the form [x, y, theta]ᵀ, with units in meters - * and radians + * unspecified or set to 0 Hz, this is 250 Hz + * on CAN FD, and 100 Hz on CAN 2.0. + * @param odometryStandardDeviation The standard deviation for odometry + * calculation in the form [x, y, theta]ᵀ, with + * units in meters and radians + * @param visionStandardDeviation The standard deviation for vision + * calculation in the form [x, y, theta]ᵀ, with + * units in meters and radians * @param modules Constants for each specific module */ - public CommandSwerveDrivetrain( - SwerveDrivetrainConstants drivetrainConstants, - double odometryUpdateFrequency, - Matrix odometryStandardDeviation, - Matrix visionStandardDeviation, - SwerveModuleConstants... modules - ) { - super(drivetrainConstants, odometryUpdateFrequency, odometryStandardDeviation, visionStandardDeviation, modules); + public CommandSwerveDrivetrain(SwerveDrivetrainConstants drivetrainConstants, double odometryUpdateFrequency, + Matrix odometryStandardDeviation, Matrix visionStandardDeviation, + SwerveModuleConstants... modules) { + super(drivetrainConstants, odometryUpdateFrequency, odometryStandardDeviation, visionStandardDeviation, + modules); if (Utils.isSimulation()) { startSimThread(); } @@ -197,34 +176,33 @@ public CommandSwerveDrivetrain( private void configureAutoBuilder() { try { var config = RobotConfig.fromGUISettings(); - AutoBuilder.configure( - () -> getState().Pose, // Supplier of current robot pose - this::resetPose, // Consumer for seeding pose against auto - () -> getState().Speeds, // Supplier of current robot speeds - // Consumer of ChassisSpeeds and feedforwards to drive the robot - (speeds, feedforwards) -> setControl( - m_pathApplyRobotSpeeds.withSpeeds(speeds) - .withWheelForceFeedforwardsX(feedforwards.robotRelativeForcesXNewtons()) - .withWheelForceFeedforwardsY(feedforwards.robotRelativeForcesYNewtons()) - ), - new PPHolonomicDriveController( - // PID constants for translation - new PIDConstants(10, 0, 0), - // PID constants for rotation - new PIDConstants(7, 0, 0) - ), - config, - // Assume the path needs to be flipped for Red vs Blue, this is normally the case - () -> DriverStation.getAlliance().orElse(Alliance.Blue) == Alliance.Red, - this // Subsystem for requirements + AutoBuilder.configure(() -> getState().Pose, // Supplier of current robot pose + this::resetPose, // Consumer for seeding pose against auto + () -> getState().Speeds, // Supplier of current robot speeds + // Consumer of ChassisSpeeds and feedforwards to drive the robot + (speeds, feedforwards) -> setControl(m_pathApplyRobotSpeeds.withSpeeds(speeds) + .withWheelForceFeedforwardsX(feedforwards.robotRelativeForcesXNewtons()) + .withWheelForceFeedforwardsY(feedforwards.robotRelativeForcesYNewtons())), + new PPHolonomicDriveController( + // PID constants for translation + new PIDConstants(10, 0, 0), + // PID constants for rotation + new PIDConstants(7, 0, 0)), + config, + // Assume the path needs to be flipped for Red vs Blue, this is normally the + // case + () -> DriverStation.getAlliance().orElse(Alliance.Blue) == Alliance.Red, this // Subsystem for + // requirements ); } catch (Exception ex) { - DriverStation.reportError("Failed to load PathPlanner config and configure AutoBuilder", ex.getStackTrace()); + DriverStation.reportError("Failed to load PathPlanner config and configure AutoBuilder", + ex.getStackTrace()); } } /** - * Returns a command that applies the specified control request to this swerve drivetrain. + * Returns a command that applies the specified control request to this swerve + * drivetrain. * * @param request Function returning the request to apply * @return Command to run @@ -245,8 +223,8 @@ public Command sysIdQuasistatic(SysIdRoutine.Direction direction) { } /** - * Runs the SysId Dynamic test in the given direction for the routine - * specified by {@link #m_sysIdRoutineToApply}. + * Runs the SysId Dynamic test in the given direction for the routine specified + * by {@link #m_sysIdRoutineToApply}. * * @param direction Direction of the SysId Dynamic test * @return Command to run @@ -258,19 +236,17 @@ public Command sysIdDynamic(SysIdRoutine.Direction direction) { @Override public void periodic() { /* - * Periodically try to apply the operator perspective. - * If we haven't applied the operator perspective before, then we should apply it regardless of DS state. - * This allows us to correct the perspective in case the robot code restarts mid-match. - * Otherwise, only check and apply the operator perspective if the DS is disabled. - * This ensures driving behavior doesn't change until an explicit disable event occurs during testing. + * Periodically try to apply the operator perspective. If we haven't applied the + * operator perspective before, then we should apply it regardless of DS state. + * This allows us to correct the perspective in case the robot code restarts + * mid-match. Otherwise, only check and apply the operator perspective if the DS + * is disabled. This ensures driving behavior doesn't change until an explicit + * disable event occurs during testing. */ if (!m_hasAppliedOperatorPerspective || DriverStation.isDisabled()) { DriverStation.getAlliance().ifPresent(allianceColor -> { - setOperatorPerspectiveForward( - allianceColor == Alliance.Red - ? kRedAlliancePerspectiveRotation - : kBlueAlliancePerspectiveRotation - ); + setOperatorPerspectiveForward(allianceColor == Alliance.Red ? kRedAlliancePerspectiveRotation + : kBlueAlliancePerspectiveRotation); m_hasAppliedOperatorPerspective = true; }); } @@ -292,11 +268,13 @@ private void startSimThread() { } /** - * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate - * while still accounting for measurement noise. + * Adds a vision measurement to the Kalman Filter. This will correct the + * odometry pose estimate while still accounting for measurement noise. * - * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera. - * @param timestampSeconds The timestamp of the vision measurement in seconds. + * @param visionRobotPoseMeters The pose of the robot as measured by the vision + * camera. + * @param timestampSeconds The timestamp of the vision measurement in + * seconds. */ @Override public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds) { @@ -304,24 +282,25 @@ public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampS } /** - * Adds a vision measurement to the Kalman Filter. This will correct the odometry pose estimate - * while still accounting for measurement noise. + * Adds a vision measurement to the Kalman Filter. This will correct the + * odometry pose estimate while still accounting for measurement noise. *

* Note that the vision measurement standard deviations passed into this method * will continue to apply to future measurements until a subsequent call to * {@link #setVisionMeasurementStdDevs(Matrix)} or this method. * - * @param visionRobotPoseMeters The pose of the robot as measured by the vision camera. - * @param timestampSeconds The timestamp of the vision measurement in seconds. - * @param visionMeasurementStdDevs Standard deviations of the vision pose measurement - * in the form [x, y, theta]ᵀ, with units in meters and radians. + * @param visionRobotPoseMeters The pose of the robot as measured by the + * vision camera. + * @param timestampSeconds The timestamp of the vision measurement in + * seconds. + * @param visionMeasurementStdDevs Standard deviations of the vision pose + * measurement in the form [x, y, theta]ᵀ, with + * units in meters and radians. */ @Override - public void addVisionMeasurement( - Pose2d visionRobotPoseMeters, - double timestampSeconds, - Matrix visionMeasurementStdDevs - ) { - super.addVisionMeasurement(visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds), visionMeasurementStdDevs); + public void addVisionMeasurement(Pose2d visionRobotPoseMeters, double timestampSeconds, + Matrix visionMeasurementStdDevs) { + super.addVisionMeasurement(visionRobotPoseMeters, Utils.fpgaToCurrentTime(timestampSeconds), + visionMeasurementStdDevs); } } From e5a780b6efd97ace7a81d5099c246fc2cb327418 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:33:54 -0600 Subject: [PATCH 23/32] Created logging class for the robot --- .../src/main/java/frc/robot/Logging.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/Logging.java diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Logging.java b/2024-2025/main-bot/src/main/java/frc/robot/Logging.java new file mode 100644 index 0000000..6090b5a --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/Logging.java @@ -0,0 +1,32 @@ +package frc.robot; + +import java.lang.Math; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; + +public class Logging { + String Name; + boolean isError; + + public Logging(String name) { + Name = name; + } + + public void log(String message) { + System.out.println(Name + ": " + message); + } + + public void error(String errorMessage) { + isError = true; + try { + throw new Error("Error:" + Name + ": " + errorMessage); + } catch (Error e) { + // This does the error thing + SmartDashboard.putBoolean(Name + ".Error", true); + } + } + + public void fixError() { + isError = false; + SmartDashboard.putBoolean(Name + ".Error", false); + } +} \ No newline at end of file From b47c79676e5b644c5ce15f05072f8b79f387584f Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:34:47 -0600 Subject: [PATCH 24/32] Updated drivetrain constant name --- .../java/frc/robot/subsystems/MapleSimSubsystem.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java index dd3cd67..03d55ef 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/MapleSimSubsystem.java @@ -12,7 +12,7 @@ import edu.wpi.first.math.system.plant.DCMotor; import edu.wpi.first.wpilibj2.command.SubsystemBase; -import frc.robot.Constants.DrivetrainConstants;; +import frc.robot.Constants.DriveTrainConstants;; public class MapleSimSubsystem extends SubsystemBase { @@ -37,11 +37,11 @@ public MapleSimSubsystem(CommandSwerveDrivetrain drivetrain) { COTS.WHEELS.DEFAULT_NEOPRENE_TREAD.cof, // Use the COF for Neoprene Wheels 9)) // L3 Gear ratio // Configures the track length and track width (spacing between swerve modules) - .withTrackLengthTrackWidth(Inches.of(DrivetrainConstants.lengthBetweenSwerveModules), - Inches.of(DrivetrainConstants.lengthBetweenSwerveModules)) + .withTrackLengthTrackWidth(Inches.of(DriveTrainConstants.lengthBetweenSwerveModules), + Inches.of(DriveTrainConstants.lengthBetweenSwerveModules)) // Configures the bumper size (dimensions of the robot bumper) - .withBumperSize(Inches.of(DrivetrainConstants.lengthOfBumpers), - Inches.of(DrivetrainConstants.lengthOfBumpers)); + .withBumperSize(Inches.of(DriveTrainConstants.lengthOfBumpers), + Inches.of(DriveTrainConstants.lengthOfBumpers)); } public void addGamePiece() { From c90e68298032819a0ef3c795b3b0142f15048521 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:35:02 -0600 Subject: [PATCH 25/32] Updated Drivetrain constants name --- .../main/java/frc/robot/subsystems/LimeLightSubsystem.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java index 47fe8ab..90860ea 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java @@ -8,7 +8,7 @@ import edu.wpi.first.math.kinematics.SwerveModulePosition; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SubsystemBase; -import frc.robot.Constants.DrivetrainConstants; +import frc.robot.Constants.DriveTrainConstants; import frc.robot.LimelightHelpers; // import edu.first.LimelightHelpers; // import edu.first.wpilibj. @@ -135,7 +135,7 @@ public double limelight_aim_proportional() { double targetingAngularVelocity = LimelightHelpers.getTX("DriveCamera") * kP; // convert to radians per second for our drive method - targetingAngularVelocity *= DrivetrainConstants.MaxAngularRate; + targetingAngularVelocity *= DriveTrainConstants.MaxAngularRate; // invert since tx is positive when the target is to the right of the crosshair targetingAngularVelocity *= -1.0; @@ -152,7 +152,7 @@ public double limelight_aim_proportional() { public double limelight_range_proportional() { double kP = .1; double targetingForwardSpeed = LimelightHelpers.getTY("limelight") * kP; - targetingForwardSpeed *= DrivetrainConstants.MaxSpeed; + targetingForwardSpeed *= DriveTrainConstants.MaxSpeed; targetingForwardSpeed *= -1.0; return targetingForwardSpeed; } From 92453747443669c477df2729c16e02c90815f506 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Fri, 21 Feb 2025 20:35:59 -0600 Subject: [PATCH 26/32] Added climber subsystem and triggers. Also added logger to elevator subsystem --- .../main/java/frc/robot/RobotContainer.java | 61 +++++++++++-------- .../robot/subsystems/ClimberSubsystem.java | 31 ++++++++++ .../robot/subsystems/ElevatorSubsystem.java | 34 ++++++----- 3 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index e39c7e1..7e78ecd 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -8,6 +8,7 @@ * Driver Controller: * A - Brake * B - Point + * Dpad Center - climber * Back + Y - SysId Dynamic Forward * Back + X - SysId Dynamic Reverse * Start + Y - SysId Quasistatic Forward @@ -50,9 +51,11 @@ import frc.robot.subsystems.CoralArmSubsystem.CoralArmLevels; import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; import frc.robot.subsystems.AlgaeArmSubsystem; +import frc.robot.subsystems.ClimberSubsystem; import frc.robot.subsystems.CommandSwerveDrivetrain; import frc.robot.subsystems.CoralArmSubsystem; import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.button.CommandXboxController; import edu.wpi.first.wpilibj2.command.button.Trigger; import edu.wpi.first.math.geometry.Rotation2d; @@ -60,6 +63,7 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; +import edu.wpi.first.wpilibj.DigitalInput; import frc.robot.subsystems.ElevatorSubsystem.ElevatorPresets; /** @@ -71,7 +75,6 @@ */ public class RobotContainer { // The robot's subsystems and commands are defined here... - // private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); private final ElevatorSubsystem m_ElevatorSubsystem = new ElevatorSubsystem( Constants.ElevatorConstants.kElevatorLeftMotorPort, Constants.ElevatorConstants.kElevatorRightMotorPort); @@ -79,18 +82,25 @@ public class RobotContainer { Constants.AlgaeArmConstants.algaeArmBarID); private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem( Constants.CoralArmConstants.coralArmID); + private final ClimberSubsystem m_ClimberSubsystem = new ClimberSubsystem( + Constants.ClimberConstants.kLeftClimberMotorID, + Constants.ClimberConstants.kRightClimberMotorID); + + // Controllers private final CommandXboxController m_driverController = new CommandXboxController( OperatorConstants.kDriverControllerPort); private final CommandXboxController m_auxillaryController = new CommandXboxController( Constants.OperatorConstants.kAuxiliaryControllerPort); private final SendableChooser autoChooser; + + DigitalInput climberLimitSwitch = new DigitalInput(Constants.ClimberConstants.kClimberLimitSwitchID); /* Swerve drive platform setup */ // From Swerve Project Generator // kSpeedAt12Volts Desired Top speed← - private double MaxSpeed = Constants.DrivetrainConstants.MaxSpeed; + private double MaxSpeed = Constants.DriveTrainConstants.MaxSpeed; // 3/4 of a rotation per second max angular velocity - private double MaxAngularRate = Constants.DrivetrainConstants.MaxAngularRate; + private double MaxAngularRate = Constants.DriveTrainConstants.MaxAngularRate; /* Setting up bindings for necessary control of the swerve drive platform */ private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) @@ -185,34 +195,31 @@ private void configureBindings(double random) { // interrupts the command, causing motors to STOP .onFalse(m_AlgaeArmSubsystem.commandInterrupt()); - // The code below was taken out to streamline the Algae arm (we need two buttons - // to control the algae arm, the method below uses 3 buttons) - // // makes algae bar spin faster if A pressed, else stop - // m_auxillaryController.povUp().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(true, - // false, false)) - // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); - // // makes algae bar spit out ball if B pressed, else stop - // m_auxillaryController.povDown().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, - // true, false)) - // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); - // // makes algae bar spin slowly if Y pressed, else stop - // m_auxillaryController.povLeft().whileTrue(m_AlgaeArmSubsystem.commandMakeBarSpin(false, - // false, true)) - // .onFalse(m_AlgaeArmSubsystem.commandMakeBarSpin(false, false, false)); - - // Command Compositions for Elevator + Coral + // ====================== Climber Subsystem ====================== // + Trigger climberTrigger = new Trigger(() -> climberLimitSwitch.get()); + climberTrigger.onTrue(m_ClimberSubsystem.runOnce(() -> m_ClimberSubsystem.climberSwitchTriggered())); + m_auxillaryController.povCenter() + .onTrue(m_ClimberSubsystem.runOnce(() -> m_ClimberSubsystem.engageClimber())); + // ====================== Elevator Preset Compositions ====================== // // Level 1 Preset // m_auxillaryController.a() // .onTrue(Commands.sequence( - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level1) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Level1)), - // m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Up)), - // Commands.waitSeconds(.5), // Allow the driver time to move up to the reef + // m_ElevatorSubsystem + // .commandMoveToPreset(ElevatorPresets.Level1) + // .until(m_ElevatorSubsystem.isElevatorAtDesiredState( + // ElevatorPresets.Level1)), + // m_CoralArmSubsystem + // .CommandsetCoralArmVoltage(0.1, CoralArmLevels.Up) + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState( + // CoralArmLevels.Up)), + // Commands.waitSeconds(4.2), // Allow the driver time to move up to the + // // reef // m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.1, CoralArmLevels.Down) - // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState(CoralArmLevels.Down)), - // m_elevatorSubsystem.commandMoveToPreset(ElevatorPresets.Lowest) - // .until(m_elevatorSubsystem.isElevatorAtDesiredState(ElevatorPresets.Highest)))); + // .until(m_CoralArmSubsystem.isCoralArmAtDesiredState( + // CoralArmLevels.Down)), + // m_ElevatorSubsystem.commandMoveToPreset(ElevatorPresets.Level1) + // .until(m_ElevatorSubsystem.isElevatorAtDesiredState( + // ElevatorPresets.Level4)))); // // Level 2 Preset // m_auxillaryController.b() // .onTrue(Commands.sequence( diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java new file mode 100644 index 0000000..84efab3 --- /dev/null +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java @@ -0,0 +1,31 @@ +package frc.robot.subsystems; + +import com.ctre.phoenix6.hardware.TalonFX; + +import edu.wpi.first.wpilibj.DigitalInput; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.Logging; + +// Must change logging + +public class ClimberSubsystem extends SubsystemBase { + private boolean switchFlipped = false; + private TalonFX leftClimberMotor; + private TalonFX rightClimberMotor; + + public ClimberSubsystem(int leftClimberMotorID, int rightClimberMotorID) { + + leftClimberMotor = new TalonFX(leftClimberMotorID); + rightClimberMotor = new TalonFX(rightClimberMotorID); + } + + public void climberSwitchTriggered() { + switchFlipped = true; + } + + public void engageClimber() { + leftClimberMotor.set(0.1); + rightClimberMotor.set(-0.1); + } + +} \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index e7a4359..405118d 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -5,7 +5,7 @@ import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import edu.wpi.first.wpilibj.Encoder; +import frc.robot.Logging; import java.util.function.BooleanSupplier; @@ -15,7 +15,7 @@ import frc.robot.Constants.ElevatorConstants; public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsystem into an actual subsystem - + private final Logging elevatorLogger; // Motors private final TalonFX m_elevatorLeftMotor; private final TalonFX m_elevatorRightMotor; @@ -36,6 +36,7 @@ public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// m_elevatorBottomLimitSwitch = new DigitalInput(8); m_Encoder1 = new Encoder(0, 1); currentElevatorState = ElevatorPresets.Level1; + elevatorLogger = new Logging("ElevatorSubsystem"); } public enum ElevatorPresets { // different levels for the coral reef @@ -66,10 +67,11 @@ public void debuggingMethod() { public boolean isElevatorAtTop() { // Placeholder - if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { return false; - } else if (m_Encoder1.get() < ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()) { - System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()) { + // System.out.println("Error: Encoder And Top Limit Switch Mismatched"); + elevatorLogger.error("Encoder And Top Limit Switch Mismatched"); return true; } else { return true; @@ -78,11 +80,12 @@ public boolean isElevatorAtTop() { public boolean isElevatorAtBottom() { // Placeholder - if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { return false; - } else if (m_Encoder1.get() > ElevatorConstants.kElevatorEncoderBottomValue + } else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue && m_elevatorBottomLimitSwitch.get()) { - System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + // System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); + elevatorLogger.error("Encoder And Bottom Limit Switch Mismatched"); return true; } else { return true; @@ -105,23 +108,22 @@ public void setMotorElevatorSpeed(double voltage) { m_elevatorRightMotor.set(0); Utility.printLn("Elevator is at the top or bottom and therefore the motors have been stopped"); } - } public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevator where to go based on certain // presets using distgusting switch case statements switch (desiredPreset) { case Level1: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); break; case Level2: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); break; case Level3: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); break; case Level4: - moveElevatorToPosition(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); break; } } @@ -139,9 +141,9 @@ public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevat * @param position * @param desiredPreset */ - public void moveElevatorToPosition(double position, ElevatorPresets desiredPreset) {// moves elevator to a certain - // position based on the encoder - // value + public void moveElevatorToPreset(double position, ElevatorPresets desiredPreset) {// moves elevator to a certain + // position based on the encoder + // value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! debuggingMethod(); if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move From 3e6034facdad9c7fbb070f2f7c8f9dc6b3614fee Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sat, 22 Feb 2025 16:59:23 -0600 Subject: [PATCH 27/32] Edits to elevator code, fixed some errors, works in simulation now --- 2024-2025/main-bot/simgui-window.json | 4 +- 2024-2025/main-bot/simgui.json | 27 ++++++- .../main/java/frc/robot/RobotContainer.java | 75 ++++++++++++++----- .../robot/subsystems/ElevatorSubsystem.java | 42 ++++++++--- 4 files changed, 118 insertions(+), 30 deletions(-) diff --git a/2024-2025/main-bot/simgui-window.json b/2024-2025/main-bot/simgui-window.json index 42b95bd..785b5de 100644 --- a/2024-2025/main-bot/simgui-window.json +++ b/2024-2025/main-bot/simgui-window.json @@ -46,7 +46,7 @@ }, "###Other Devices": { "Collapsed": "0", - "Pos": "1651,30", + "Pos": "1650,32", "Size": "250,695" }, "###Plot <0>": { @@ -67,7 +67,7 @@ "###Timing": { "Collapsed": "0", "Pos": "5,150", - "Size": "135,150" + "Size": "135,173" }, "Debug##Default": { "Collapsed": "0", diff --git a/2024-2025/main-bot/simgui.json b/2024-2025/main-bot/simgui.json index d368b5e..307e52a 100644 --- a/2024-2025/main-bot/simgui.json +++ b/2024-2025/main-bot/simgui.json @@ -1,4 +1,18 @@ { + "HALProvider": { + "Other Devices": { + "Talon FX (v6)[12]": { + "header": { + "open": true + } + }, + "Talon FX (v6)[13]": { + "header": { + "open": true + } + } + } + }, "NTProvider": { "types": { "/FMSInfo": "FMSInfo", @@ -62,7 +76,18 @@ 0.0, 0.8500000238418579 ], - "height": 338 + "height": 338, + "series": [ + { + "color": [ + 0.2980392277240753, + 0.44705885648727417, + 0.6901960968971252, + 1.0 + ], + "id": "CANMotor:Talon FX (v6)[12]-motorVoltage" + } + ] } ] } diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index 7e78ecd..e81db04 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -56,6 +56,7 @@ import frc.robot.subsystems.CoralArmSubsystem; import edu.wpi.first.wpilibj2.command.Command; 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 edu.wpi.first.math.geometry.Rotation2d; @@ -98,9 +99,9 @@ public class RobotContainer { /* Swerve drive platform setup */ // From Swerve Project Generator // kSpeedAt12Volts Desired Top speed← - private double MaxSpeed = Constants.DriveTrainConstants.MaxSpeed; + private double MaxSpeed = TunerConstants.kSpeedAt12Volts.in(MetersPerSecond); // 3/4 of a rotation per second max angular velocity - private double MaxAngularRate = Constants.DriveTrainConstants.MaxAngularRate; + private double MaxAngularRate = RotationsPerSecond.of(0.75).in(RadiansPerSecond); /* Setting up bindings for necessary control of the swerve drive platform */ private final SwerveRequest.FieldCentric drive = new SwerveRequest.FieldCentric().withDeadband(MaxSpeed * 0.1) @@ -123,6 +124,7 @@ public class RobotContainer { * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { + // Build an auto chooser. This will use Commands.none() as the default option. autoChooser = AutoBuilder.buildAutoChooser(); @@ -148,7 +150,7 @@ public RobotContainer() { * controllers or {@link edu.wpi.first.wpilibj2.command.button.CommandJoystick * Flight joysticks}. * - * @param random TODO + * @param random */ private void configureBindings(double random) { @@ -158,26 +160,39 @@ private void configureBindings(double random) { // Sets the default command for the elevator (the command that always runs) // Checks if the left joystick is being moved, if it is, it will run the // commandVoltage method with the left joystick's Y value - m_ElevatorSubsystem.setDefaultCommand((Math.abs(m_auxillaryController.getLeftY()) > 0.1) - ? m_ElevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) - : m_ElevatorSubsystem.commandVoltage(0)); - // 10 is a PLACEHOLDER value for now + // m_ElevatorSubsystem.setDefaultCommand(((Math.abs(m_auxillaryController.getLeftY()) + // > 0.1) && (testMethod())) + // ? m_ElevatorSubsystem.commandVoltage(m_auxillaryController.getLeftY()) + // : m_ElevatorSubsystem.commandVoltage(0)); + + new Trigger(() -> m_auxillaryController.getLeftY() > 0.1).whileTrue(new InstantCommand(() -> { + SmartDashboard.putBoolean("Going Up Triggered", true); + SmartDashboard.putNumber("Y-value", m_auxillaryController.getLeftY()); + m_ElevatorSubsystem.setMotorElevatorSpeed(0.2); + }, m_ElevatorSubsystem)).onFalse( + new InstantCommand(() -> SmartDashboard.putBoolean("Going Up Triggered", false))); + + new Trigger(() -> m_auxillaryController.getLeftY() < -0.1).whileTrue(new InstantCommand(() -> { + SmartDashboard.putBoolean("Going Down triggered", true); + SmartDashboard.putNumber("Y-value", m_auxillaryController.getLeftY()); + m_ElevatorSubsystem.setMotorElevatorSpeed(-0.2); + })).onFalse(new InstantCommand(() -> SmartDashboard.putBoolean("Going Down triggered", false))); // The commands below make the elevator go to certain levels m_auxillaryController.a() - .onTrue(new RunCommand( + .onTrue(new InstantCommand( () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level1), m_ElevatorSubsystem)); m_auxillaryController.b() - .onTrue(new RunCommand( + .onTrue(new InstantCommand( () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level2), m_ElevatorSubsystem)); - m_auxillaryController.x() - .onTrue(new RunCommand( + m_auxillaryController.y() + .onTrue(new InstantCommand( () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level3), m_ElevatorSubsystem)); - m_auxillaryController.y() - .onTrue(new RunCommand( + m_auxillaryController.x() + .onTrue(new InstantCommand( () -> m_ElevatorSubsystem.moveElevatorToPreset(ElevatorPresets.Level4), m_ElevatorSubsystem)); @@ -260,17 +275,15 @@ private void configureBindings(double random) { // ====================== Coral Arm Subsystem ====================== // // makes coral arm go up m_auxillaryController.leftBumper() - .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.5, CoralArmLevels.Up)); + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0.2, CoralArmLevels.Up)); // makes coral arm go down m_auxillaryController.rightBumper() - .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.5, CoralArmLevels.Down)); + .whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(-0.2, CoralArmLevels.Down)); // Emergency Stop for Coral Arm (in case it goes past the top or bottom) Note: I // don't know if the onTrue method will only run once, so test it before you use // it m_auxillaryController.povCenter().onTrue(m_CoralArmSubsystem.emergencyStop()); // stops coral arm - // m_auxillaryController.povCenter().whileTrue(m_CoralArmSubsystem.CommandsetCoralArmVoltage(0, - // CoralArmLevels.Stop)); // ====================== Drive Subsystem ====================== // // Code below is from swerve drive project generator @@ -329,3 +342,31 @@ public Command getAutonomousCommand() { } // another why not comment :) // I do love some good comments :D + +// Saw this code in another teams robot and took it, may be interesting to +// implement +// Endgame alert triggers + +// new Trigger( +// () -> +// DriverStation.isTeleopEnabled() +// && DriverStation.getMatchTime() > 0 +// && DriverStation.getMatchTime() <= Math.round(endgameAlert1.get())) +// .onTrue( +// controllerRumbleCommand() +// .withTimeout(0.5) +// .beforeStarting(() -> Leds.getInstance().endgameAlert = true) +// .finallyDo(() -> Leds.getInstance().endgameAlert = false)); +// new Trigger( +// () -> +// DriverStation.isTeleopEnabled() +// && DriverStation.getMatchTime() > 0 +// && DriverStation.getMatchTime() <= Math.round(endgameAlert2.get())) +// .onTrue( +// controllerRumbleCommand() +// .withTimeout(0.2) +// .andThen(Commands.waitSeconds(0.1)) +// .repeatedly() +// .withTimeout(0.9) // Rumble three times +// .beforeStarting(() -> Leds.getInstance().endgameAlert = true) +// .finallyDo(() -> Leds.getInstance().endgameAlert = false)); \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index 405118d..b443967 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -3,9 +3,12 @@ import edu.wpi.first.wpilibj2.command.SubsystemBase; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.FunctionalCommand; import edu.wpi.first.wpilibj.Encoder; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.Logging; +import frc.robot.Robot; +import frc.robot.RobotContainer; import java.util.function.BooleanSupplier; @@ -37,6 +40,7 @@ public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// m_Encoder1 = new Encoder(0, 1); currentElevatorState = ElevatorPresets.Level1; elevatorLogger = new Logging("ElevatorSubsystem"); + motorElevatorSpeed = ElevatorConstants.kMotorElevatorSpeed; } public enum ElevatorPresets { // different levels for the coral reef @@ -69,7 +73,7 @@ public boolean isElevatorAtTop() { // Placeholder if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { return false; - } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue && m_elevatorTopLimitSwitch.get()) { + } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue || !m_elevatorTopLimitSwitch.get()) { // System.out.println("Error: Encoder And Top Limit Switch Mismatched"); elevatorLogger.error("Encoder And Top Limit Switch Mismatched"); return true; @@ -83,7 +87,7 @@ public boolean isElevatorAtBottom() { if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { return false; } else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue - && m_elevatorBottomLimitSwitch.get()) { + || !m_elevatorBottomLimitSwitch.get()) { // System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); elevatorLogger.error("Encoder And Bottom Limit Switch Mismatched"); return true; @@ -98,15 +102,28 @@ public void setMotorElevatorSpeed(double voltage) { if (voltage > 0 && !isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); + // Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + + // m_Encoder1.get() + // + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() + // + "\n Elevator Bottom Limit Switch Value: " + + // m_elevatorBottomLimitSwitch.get()); Utility.printLn("Elevator is moving up"); } else if (voltage < 0 && !isElevatorAtBottom()) {// or if its at bottom make move down m_elevatorLeftMotor.set(voltage); m_elevatorRightMotor.set(-voltage); + // Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + + // m_Encoder1.get() + // + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() + // + "\n Elevator Bottom Limit Switch Value: " + + // m_elevatorBottomLimitSwitch.get()); Utility.printLn("Elevator is moving down"); } else {// otherwise just no speed m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); - Utility.printLn("Elevator is at the top or bottom and therefore the motors have been stopped"); + Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + m_Encoder1.get() + + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() + + "\n Elevator Bottom Limit Switch Value: " + m_elevatorBottomLimitSwitch.get()); + Utility.printLn("Elevator is at the top, bottom, or desired position. motors have been stopped"); } } @@ -114,16 +131,20 @@ public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevat // presets using distgusting switch case statements switch (desiredPreset) { case Level1: - moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + SmartDashboard.putString("DesiredPreset", "Level1"); + moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); break; case Level2: - moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + SmartDashboard.putString("DesiredPreset", "Level2"); + moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); break; case Level3: - moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + SmartDashboard.putString("DesiredPreset", "Level3"); + moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); break; case Level4: - moveElevatorToPreset(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + SmartDashboard.putString("DesiredPreset", "Level4"); + moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); break; } } @@ -141,9 +162,10 @@ public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevat * @param position * @param desiredPreset */ - public void moveElevatorToPreset(double position, ElevatorPresets desiredPreset) {// moves elevator to a certain - // position based on the encoder - // value + public void moveElevatorToSpecificPreset(double position, ElevatorPresets desiredPreset) {// moves elevator to a + // certain + // position based on the encoder + // value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! debuggingMethod(); if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move From 99997aa3842e8e93bc135211f61d1f2ed14429f6 Mon Sep 17 00:00:00 2001 From: Henry Hare Date: Sun, 23 Feb 2025 15:37:21 -0600 Subject: [PATCH 28/32] Changed subsystems to directly import constants, also removed some unused code in Elevator Subystem --- .../src/main/java/frc/robot/Constants.java | 16 +- .../main/java/frc/robot/RobotContainer.java | 20 +-- .../robot/subsystems/AlgaeArmSubsystem.java | 5 +- .../robot/subsystems/ClimberSubsystem.java | 8 +- .../robot/subsystems/CoralArmSubsystem.java | 91 +++++------- .../robot/subsystems/ElevatorSubsystem.java | 140 ++++++++++-------- .../robot/subsystems/LimeLightSubsystem.java | 5 + 7 files changed, 147 insertions(+), 138 deletions(-) diff --git a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java index d5f6a4f..baac14d 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/Constants.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/Constants.java @@ -20,7 +20,7 @@ * 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 class Constants { public static class OperatorConstants { public static final int kDriverControllerPort = 0; public static final int kAuxiliaryControllerPort = 1; @@ -28,9 +28,13 @@ public static class OperatorConstants { // PLACEHOLDER IDS!!! public static class ElevatorConstants { - public static final int kElevatorLeftMotorPort = 12; - public static final int kElevatorRightMotorPort = 13; + public static final int kElevatorLeftMotorID = 12; + public static final int kElevatorRightMotorID = 13; public static final double kMotorElevatorSpeed = 0.2; + public static final int kLeftElevatorEncoderID1 = 0; + public static final int kLeftElevatorEncoderID2 = 1; + public static final int kRightElevatorEncoderID1 = 2; + public static final int kRightElevatorEncoderID2 = 3; public static final int kElevatorEncoderBottomValue = 0; public static final int kElevatorEncoderTopValue = 1000; @@ -44,11 +48,13 @@ public static class ElevatorPreset { } public static final class AlgaeArmConstants { - public static final int algaeArmBarID = 8;// placeholder + public static final int algaeArmBarMotorID = 8;// placeholder } public static class CoralArmConstants { - public static final int coralArmID = 69;// placeholder with funny number hehe + public static final int coralArmMotorID = 69;// placeholder with funny number hehe + public static final int kCoralEncoderID1 = 4; + public static final int kCoralEncoderID2 = 5; public static final int kCoralEncoderTopValue = 1000; public static final int kCoralEncoderTopBuffer = kCoralEncoderTopValue + 10; public static final int kCoralEncoderBottomValue = 0; diff --git a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java index e81db04..24858fa 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/RobotContainer.java @@ -75,17 +75,11 @@ * subsystems, commands, and trigger mappings) should be declared here. */ public class RobotContainer { - // The robot's subsystems and commands are defined here... - private final ElevatorSubsystem m_ElevatorSubsystem = new ElevatorSubsystem( - Constants.ElevatorConstants.kElevatorLeftMotorPort, - Constants.ElevatorConstants.kElevatorRightMotorPort); - private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem( - Constants.AlgaeArmConstants.algaeArmBarID); - private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem( - Constants.CoralArmConstants.coralArmID); - private final ClimberSubsystem m_ClimberSubsystem = new ClimberSubsystem( - Constants.ClimberConstants.kLeftClimberMotorID, - Constants.ClimberConstants.kRightClimberMotorID); + // The robot's subsystems and commands are defined here...ElevatorConstants + private final ElevatorSubsystem m_ElevatorSubsystem = new ElevatorSubsystem(); + private final AlgaeArmSubsystem m_AlgaeArmSubsystem = new AlgaeArmSubsystem(); + private final CoralArmSubsystem m_CoralArmSubsystem = new CoralArmSubsystem(); + private final ClimberSubsystem m_ClimberSubsystem = new ClimberSubsystem(); // Controllers private final CommandXboxController m_driverController = new CommandXboxController( @@ -168,14 +162,14 @@ private void configureBindings(double random) { new Trigger(() -> m_auxillaryController.getLeftY() > 0.1).whileTrue(new InstantCommand(() -> { SmartDashboard.putBoolean("Going Up Triggered", true); SmartDashboard.putNumber("Y-value", m_auxillaryController.getLeftY()); - m_ElevatorSubsystem.setMotorElevatorSpeed(0.2); + m_ElevatorSubsystem.setMotorElevatorSpeed(true); }, m_ElevatorSubsystem)).onFalse( new InstantCommand(() -> SmartDashboard.putBoolean("Going Up Triggered", false))); new Trigger(() -> m_auxillaryController.getLeftY() < -0.1).whileTrue(new InstantCommand(() -> { SmartDashboard.putBoolean("Going Down triggered", true); SmartDashboard.putNumber("Y-value", m_auxillaryController.getLeftY()); - m_ElevatorSubsystem.setMotorElevatorSpeed(-0.2); + m_ElevatorSubsystem.setMotorElevatorSpeed(false); })).onFalse(new InstantCommand(() -> SmartDashboard.putBoolean("Going Down triggered", false))); // The commands below make the elevator go to certain levels diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java index 78cf0b4..a11ebbb 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/AlgaeArmSubsystem.java @@ -10,6 +10,7 @@ //import edu.wpi.first.wpilibj2.command.button.CommandXboxController; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; +import frc.robot.Constants.AlgaeArmConstants; public class AlgaeArmSubsystem extends SubsystemBase { @@ -17,8 +18,8 @@ public class AlgaeArmSubsystem extends SubsystemBase { private final TalonSRX m_algaeBarMotor; // Initialized Stuff - public AlgaeArmSubsystem(int algaeBarMoveID) { - m_algaeBarMotor = new TalonSRX(algaeBarMoveID); + public AlgaeArmSubsystem() { + m_algaeBarMotor = new TalonSRX(AlgaeArmConstants.algaeArmBarMotorID); } public Command commandMakeBarSpin(boolean APressed, boolean BPressed, boolean YPressed) {// makes, well, the bar diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java index 84efab3..941fc67 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ClimberSubsystem.java @@ -5,6 +5,7 @@ import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Logging; +import frc.robot.Constants.ClimberConstants; // Must change logging @@ -13,10 +14,9 @@ public class ClimberSubsystem extends SubsystemBase { private TalonFX leftClimberMotor; private TalonFX rightClimberMotor; - public ClimberSubsystem(int leftClimberMotorID, int rightClimberMotorID) { - - leftClimberMotor = new TalonFX(leftClimberMotorID); - rightClimberMotor = new TalonFX(rightClimberMotorID); + public ClimberSubsystem() { + leftClimberMotor = new TalonFX(ClimberConstants.kLeftClimberMotorID); + rightClimberMotor = new TalonFX(ClimberConstants.kRightClimberMotorID); } public void climberSwitchTriggered() { diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java index efb215e..927c971 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/CoralArmSubsystem.java @@ -1,4 +1,5 @@ package frc.robot.subsystems; + //"A good programmers code needs not any comments, for it comments on itself" - IDK who I made it up import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants.CoralArmConstants; @@ -9,67 +10,58 @@ import com.ctre.phoenix.motorcontrol.can.TalonSRX; import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj.Encoder; - -public class CoralArmSubsystem extends SubsystemBase{ - //Motor(s) + +public class CoralArmSubsystem extends SubsystemBase { + // Motor(s) private final TalonSRX m_CoralArmMotor; - //Encoder + // Encoder private final Encoder m_coralEncoder; // Current state private CoralArmLevels currentCoralArmState; - public CoralArmSubsystem(int coralArmID) { - //Initialized Stuff - m_coralEncoder = new Encoder(2,3); - m_CoralArmMotor = new TalonSRX(coralArmID); + public CoralArmSubsystem() { + // Initialized Stuff + m_coralEncoder = new Encoder(CoralArmConstants.kCoralEncoderID1, CoralArmConstants.kCoralEncoderID2); + m_CoralArmMotor = new TalonSRX(CoralArmConstants.coralArmMotorID); currentCoralArmState = CoralArmLevels.Down; } public enum CoralArmLevels { - Down, - Up, - Stop + Down, Up, Stop } public Command CommandsetCoralArmVoltage(double voltage, CoralArmLevels desiredState) { - return runOnce( - () -> { - if (voltage > 0) { + return runOnce(() -> { + if (voltage > 0) { if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue) { setCoralArmVoltage(voltage); - } - else if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopBuffer) { + } else if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopBuffer) { System.out.println("How is the arm not broken yet? Amazingly, you didn't break it."); setCoralArmVoltage(0); - } - else { + } else { setCoralArmVoltage(0); currentCoralArmState = desiredState; } - } - else if (voltage < 0) { + } else if (voltage < 0) { if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderBottomValue) { setCoralArmVoltage(voltage); - } - else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderBottomBuffer) { + } else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderBottomBuffer) { System.out.println("How is the arm not broken yet? Amazingly, you didn't break it."); setCoralArmVoltage(0); - } - else { + } else { setCoralArmVoltage(0); currentCoralArmState = desiredState; } - } - else { + } else { setCoralArmVoltage(0); currentCoralArmState = desiredState; - } } - ); + }); } + public void setCoralArmVoltage(double voltage) { m_CoralArmMotor.set(ControlMode.PercentOutput, voltage); - //Add more motors here if neccessary + // Add more motors here if neccessary } public CoralArmLevels getCurrentCoralArmState() { @@ -80,29 +72,26 @@ public BooleanSupplier isCoralArmAtDesiredState(CoralArmLevels desiredState) { return () -> currentCoralArmState == desiredState; } - public Command emergencyStop() {// Just in case the driver wants to stop the arm without stopping the whole program (and for some reason the Command Voltage function doesn't stop the arm after it gets above a certain point). - return runOnce ( - () -> { + public Command emergencyStop() {// Just in case the driver wants to stop the arm without stopping the whole + // program (and for some reason the Command Voltage function doesn't stop the + // arm after it gets above a certain point). + return runOnce(() -> { + setCoralArmVoltage(0); + System.out.println("Emergency Stop Pressed! Stay Still And Don't Move The Arm..."); + if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue) { + do {// Hey look at that, a do while loop has a use! + setCoralArmVoltage(-0.25); + } while (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue); setCoralArmVoltage(0); - System.out.println("Emergency Stop Pressed! Stay Still And Don't Move The Arm..."); - if (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue) { - do {//Hey look at that, a do while loop has a use! - setCoralArmVoltage(-0.25); - } while (m_coralEncoder.get() > CoralArmConstants.kCoralEncoderTopValue); - setCoralArmVoltage(0); - } - else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue) { - do { - setCoralArmVoltage(0.25); - } while (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue); - setCoralArmVoltage(0); - } - else { - System.out.println("Arm is already at top"); - } - System.out.println("Arm Is Now Reset. Carry On."); + } else if (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue) { + do { + setCoralArmVoltage(0.25); + } while (m_coralEncoder.get() < CoralArmConstants.kCoralEncoderTopValue); + setCoralArmVoltage(0); + } else { + System.out.println("Arm is already at top"); } - ); + System.out.println("Arm Is Now Reset. Carry On."); + }); } } - \ No newline at end of file diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java index b443967..add87d4 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/ElevatorSubsystem.java @@ -9,13 +9,13 @@ import frc.robot.Logging; import frc.robot.Robot; import frc.robot.RobotContainer; +import frc.robot.Constants.ElevatorConstants; import java.util.function.BooleanSupplier; //import com.ctre.phoenix.Util; import com.ctre.phoenix6.hardware.TalonFX; import frc.robot.Utility; -import frc.robot.Constants.ElevatorConstants; public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsystem into an actual subsystem private final Logging elevatorLogger; @@ -26,54 +26,74 @@ public class ElevatorSubsystem extends SubsystemBase {// makes elevator subsyste private DigitalInput m_elevatorTopLimitSwitch; private DigitalInput m_elevatorBottomLimitSwitch; // Encoder - private final Encoder m_Encoder1; + private final Encoder m_leftElevatorEncoder; + private final Encoder m_rightElevatorEncoder; private ElevatorPresets currentElevatorState; // Constants - private double motorElevatorSpeed; - - public ElevatorSubsystem(int elevatorLeftMotorId, int elevatorRightMotorId) {// this is da place where stuff is - // actually initialized - m_elevatorLeftMotor = new TalonFX(elevatorLeftMotorId); - m_elevatorRightMotor = new TalonFX(elevatorRightMotorId); + private double motorElevatorSpeed; // Might take out in favor of motion magic + private final double elevatorEncoderTopValue; + private final double elevatorEncoderBottomValue; + private final double preset1EncoderValue; + private final double preset2EncoderValue; + private final double preset3EncoderValue; + private final double preset4EncoderValue; + + public ElevatorSubsystem() {// constructor for the elevator subsystem + // Motor Initialization + m_elevatorLeftMotor = new TalonFX(ElevatorConstants.kElevatorLeftMotorID); + m_elevatorRightMotor = new TalonFX(ElevatorConstants.kElevatorRightMotorID); + // Limit Switch Initialization m_elevatorTopLimitSwitch = new DigitalInput(7); m_elevatorBottomLimitSwitch = new DigitalInput(8); - m_Encoder1 = new Encoder(0, 1); + // Encoder Initialization + m_leftElevatorEncoder = new Encoder(ElevatorConstants.kLeftElevatorEncoderID1, + ElevatorConstants.kLeftElevatorEncoderID2); + m_rightElevatorEncoder = new Encoder(ElevatorConstants.kRightElevatorEncoderID1, + ElevatorConstants.kRightElevatorEncoderID2); + // Current State Initialization currentElevatorState = ElevatorPresets.Level1; elevatorLogger = new Logging("ElevatorSubsystem"); + // Constants Initialization motorElevatorSpeed = ElevatorConstants.kMotorElevatorSpeed; + elevatorEncoderTopValue = ElevatorConstants.kElevatorEncoderTopValue; + elevatorEncoderBottomValue = ElevatorConstants.kElevatorEncoderBottomValue; + preset1EncoderValue = ElevatorConstants.ElevatorPreset.level1EncoderValue; + preset2EncoderValue = ElevatorConstants.ElevatorPreset.level2EncoderValue; + preset3EncoderValue = ElevatorConstants.ElevatorPreset.level3EncoderValue; + preset4EncoderValue = ElevatorConstants.ElevatorPreset.level4EncoderValue; } public enum ElevatorPresets { // different levels for the coral reef Level1, Level2, Level3, Level4 } - public Command raiseElevator() { - // Placeholder - return runOnce(() -> { - /* one-time action goes here */ - setMotorElevatorSpeed(0.1); - }); - } - - public Command lowerElevator() { - // Placeholder - return runOnce(() -> { - /* one-time action goes here */ - setMotorElevatorSpeed(-0.1); - }); - } + // public Command raiseElevator() { + // // Placeholder + // return runOnce(() -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(0.1); + // }); + // } + + // public Command lowerElevator() { + // // Placeholder + // return runOnce(() -> { + // /* one-time action goes here */ + // setMotorElevatorSpeed(-0.1); + // }); + // } public void debuggingMethod() { - SmartDashboard.putNumber("Elevator Encoder", m_Encoder1.get()); + SmartDashboard.putNumber("Elevator Encoder", m_leftElevatorEncoder.get()); SmartDashboard.putBoolean("Elevator Top Limit Switch", m_elevatorTopLimitSwitch.get()); SmartDashboard.putBoolean("Elevator Bottom Limit Switch", m_elevatorBottomLimitSwitch.get()); } public boolean isElevatorAtTop() { // Placeholder - if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { + if (m_leftElevatorEncoder.get() <= elevatorEncoderTopValue && !m_elevatorTopLimitSwitch.get()) { return false; - } else if (m_Encoder1.get() <= ElevatorConstants.kElevatorEncoderTopValue || !m_elevatorTopLimitSwitch.get()) { + } else if (m_leftElevatorEncoder.get() <= elevatorEncoderBottomValue || !m_elevatorTopLimitSwitch.get()) { // System.out.println("Error: Encoder And Top Limit Switch Mismatched"); elevatorLogger.error("Encoder And Top Limit Switch Mismatched"); return true; @@ -84,10 +104,9 @@ public boolean isElevatorAtTop() { public boolean isElevatorAtBottom() { // Placeholder - if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { + if (m_leftElevatorEncoder.get() >= elevatorEncoderBottomValue && !m_elevatorBottomLimitSwitch.get()) { return false; - } else if (m_Encoder1.get() >= ElevatorConstants.kElevatorEncoderBottomValue - || !m_elevatorBottomLimitSwitch.get()) { + } else if (m_leftElevatorEncoder.get() >= elevatorEncoderBottomValue || !m_elevatorBottomLimitSwitch.get()) { // System.out.println("Error: Encoder And Bottom Limit Switch Mismatched"); elevatorLogger.error("Encoder And Bottom Limit Switch Mismatched"); return true; @@ -96,31 +115,23 @@ public boolean isElevatorAtBottom() { } } - public void setMotorElevatorSpeed(double voltage) { + public void setMotorElevatorSpeed(boolean isGoingup) { // Placeholder debuggingMethod(); - if (voltage > 0 && !isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up - m_elevatorLeftMotor.set(voltage); - m_elevatorRightMotor.set(-voltage); - // Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + - // m_Encoder1.get() - // + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() - // + "\n Elevator Bottom Limit Switch Value: " + - // m_elevatorBottomLimitSwitch.get()); + if (isGoingup && !isElevatorAtTop()) {// if motor is moving and elevators at the top, make both move up + m_elevatorLeftMotor.set(motorElevatorSpeed); + m_elevatorRightMotor.set(-motorElevatorSpeed); Utility.printLn("Elevator is moving up"); - } else if (voltage < 0 && !isElevatorAtBottom()) {// or if its at bottom make move down - m_elevatorLeftMotor.set(voltage); - m_elevatorRightMotor.set(-voltage); - // Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + - // m_Encoder1.get() - // + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() - // + "\n Elevator Bottom Limit Switch Value: " + - // m_elevatorBottomLimitSwitch.get()); + } else if (!isGoingup && !isElevatorAtBottom()) {// or if its at bottom make move down + m_elevatorLeftMotor.set(-motorElevatorSpeed); + m_elevatorRightMotor.set(-motorElevatorSpeed); Utility.printLn("Elevator is moving down"); } else {// otherwise just no speed m_elevatorLeftMotor.set(0); m_elevatorRightMotor.set(0); - Utility.printLn("Voltage Value: " + voltage + "\n Elevator Encoder Value: " + m_Encoder1.get() + System.out.println("Motor Voltage Value: " + m_elevatorLeftMotor.getMotorVoltage() + + "\n desired Voltage Value: " + motorElevatorSpeed + "\n is elevator going up: " + isGoingup + + "\n Elevator Encoder Value: " + m_leftElevatorEncoder.get() + "\n Elevator Top Limit Switch Value: " + m_elevatorTopLimitSwitch.get() + "\n Elevator Bottom Limit Switch Value: " + m_elevatorBottomLimitSwitch.get()); Utility.printLn("Elevator is at the top, bottom, or desired position. motors have been stopped"); @@ -132,19 +143,19 @@ public void moveElevatorToPreset(ElevatorPresets desiredPreset) {// tells elevat switch (desiredPreset) { case Level1: SmartDashboard.putString("DesiredPreset", "Level1"); - moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level1EncoderValue, desiredPreset); + moveElevatorToSpecificPreset(preset1EncoderValue, desiredPreset); break; case Level2: SmartDashboard.putString("DesiredPreset", "Level2"); - moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level2EncoderValue, desiredPreset); + moveElevatorToSpecificPreset(preset2EncoderValue, desiredPreset); break; case Level3: SmartDashboard.putString("DesiredPreset", "Level3"); - moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level3EncoderValue, desiredPreset); + moveElevatorToSpecificPreset(preset3EncoderValue, desiredPreset); break; case Level4: SmartDashboard.putString("DesiredPreset", "Level4"); - moveElevatorToSpecificPreset(ElevatorConstants.ElevatorPreset.level4EncoderValue, desiredPreset); + moveElevatorToSpecificPreset(preset4EncoderValue, desiredPreset); break; } } @@ -168,15 +179,17 @@ public void moveElevatorToSpecificPreset(double position, ElevatorPresets desire // value // WILL HAVE TO REVERSE ONE MOTOR DEPENDING ON ORIENTATION!!!! debuggingMethod(); - if (m_Encoder1.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor wants to move - // down and makes sure the elevator isn't at - // the bottom + if (m_leftElevatorEncoder.get() <= (position + 5) && !isElevatorAtBottom()) { // checking to see if the motor + // wants to move + // down and makes sure the elevator isn't at + // the bottom m_elevatorLeftMotor.set(-motorElevatorSpeed); m_elevatorRightMotor.set(motorElevatorSpeed); Utility.printLn("Elevator is moving down"); - } else if (m_Encoder1.get() >= (position - 5) && !isElevatorAtTop()) { // checking to see if the motor wants to - // move up and makes sure the elevator - // isn't at the top + } else if (m_leftElevatorEncoder.get() >= (position - 5) && !isElevatorAtTop()) { // checking to see if the + // motor wants to + // move up and makes sure the elevator + // isn't at the top m_elevatorLeftMotor.set(motorElevatorSpeed); m_elevatorRightMotor.set(-motorElevatorSpeed); Utility.printLn("Elevator is moving up"); @@ -197,11 +210,12 @@ public BooleanSupplier isElevatorAtDesiredState(ElevatorPresets desiredState) { return () -> currentElevatorState == desiredState; } - public Command commandVoltage(double voltage) {// just exists because just felt like it - return runOnce(() -> { - setMotorElevatorSpeed(voltage); - }); - } + // public Command commandVoltage(double voltage) {// just exists because just + // felt like it + // return runOnce(() -> { + // setMotorElevatorSpeed(voltage); + // }); + // } public Command commandMoveToPreset(ElevatorPresets desiredPreset) {// The first function in a very tall dependancy // tree diff --git a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java index 90860ea..b0c8f26 100644 --- a/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java +++ b/2024-2025/main-bot/src/main/java/frc/robot/subsystems/LimeLightSubsystem.java @@ -25,6 +25,11 @@ public class LimeLightSubsystem extends SubsystemBase { + /** + * tx - Horizontal Offset ty - Vertical Offset ta - Area of target tv - Target + * Visible + */ + int FLIndex = 0; int FRIndex = 1; int BLIndex = 2; From d741151370b343585080f072321395b17e044bef Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 24 Feb 2025 12:09:18 -0600 Subject: [PATCH 29/32] Remove superlinter from workflows, will re-add later on --- .github/workflows/superlinter.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/superlinter.yml diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml deleted file mode 100644 index a20e4e9..0000000 --- a/.github/workflows/superlinter.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Super-Linter - -on: push - -jobs: - super-lint: - name: Super-Linter - runs-on: - - self-hosted - - Primary - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Run Super-Linter - uses: github/super-linter@v5 - env: - # Testing multiple folders and files - # Files to ignore: - # - gradlew - # - .gradle - # - WPILib-License.md - # Folders to ignore: - # - 2022-2023 - # - vendordeps - FILTER_REGEX_EXCLUDE: ".*2022-2023/.*|.*vendordeps/.*|.*gradlew|.*.gradle|.*WPILib-License.md.*" - DEFAULT_BRANCH: main - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 0d3395daa8f675cfb6a07dd343eedff939c87157 Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 24 Feb 2025 12:14:00 -0600 Subject: [PATCH 30/32] Added gradle-build workflow, needs changed before merging though --- .github/workflows/gradle-build.yml | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/gradle-build.yml diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml new file mode 100644 index 0000000..50ee8e3 --- /dev/null +++ b/.github/workflows/gradle-build.yml @@ -0,0 +1,59 @@ +# Gradle workflow to build the current project and generate a dependency graph for Dependabot Alerts. + +name: Gradle Dependencies and Build + +on: + push: + branches: [ "2025MainBranch" ] # Change to "main" when merged + pull_request: + branches: [ "2025MainBranch" ] # Change to "main" when merged + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + # Set up Java 17 JDK since the WPILib provided one isn't installed on this runner + # This will most likely change for a self-hosted runner + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + + - name: Build with Gradle Wrapper + # Set the working directory so gradlew and WPILib are in the right folder + # This will be removed in the future whenever this project is in the root folder + working-directory: ./2024-2025/main-bot + run: ./gradlew build + + dependency-submission: + + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + # Set up Java 17 JDK since the WPILib provided one isn't installed on this runner + # This will most likely change for a self-hosted runner + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. + # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 From 9ec2ccc21d821d7b189e932de653ee3a73a9b65a Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 24 Feb 2025 12:14:22 -0600 Subject: [PATCH 31/32] Removed old build workflow --- .github/workflows/wpilib-build.yml | 36 ------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 .github/workflows/wpilib-build.yml diff --git a/.github/workflows/wpilib-build.yml b/.github/workflows/wpilib-build.yml deleted file mode 100644 index c866985..0000000 --- a/.github/workflows/wpilib-build.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Build - -on: push - -jobs: - Build-Latest: - runs-on: - - self-hosted - - Primary - steps: - # Check out the code so the action can use it - - name: Checkout code - uses: actions/checkout@v4 - - - name: Build 2023-2024 - # Set the working directory so gradlew and WPILib are in the right folder - working-directory: ./2023-2024/main-bot - # Run gradlew to build the robot code with java home being what it is set to during manual runtime - # This machine should have Java 17 installed which is what WPILib is using for building with 2024 - run: ./gradlew build - Build-2022-2023: - runs-on: - - self-hosted - - Primary - steps: - # Check out the code so the action can use it - - name: Checkout code - uses: actions/checkout@v4 - - - name: Build 2022-2023 - working-directory: ./2022-2023/main-bot - run: ./gradlew build - - -# I have no idea if this will work but this is just to test to see if it's possible and to get used to using actions From 4e02dd277467ad357ed872d345f2793b88231b35 Mon Sep 17 00:00:00 2001 From: Jay McNeely Date: Mon, 24 Feb 2025 12:20:25 -0600 Subject: [PATCH 32/32] Updated permissions for gradlew. --- 2024-2025/main-bot/gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 2024-2025/main-bot/gradlew diff --git a/2024-2025/main-bot/gradlew b/2024-2025/main-bot/gradlew old mode 100644 new mode 100755