diff --git a/.vscode/launch.json b/.vscode/launch.json index 396fe88..831a3bb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,40 +4,38 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "wpilib", - "name": "WPILib Desktop Debug", + "type": "java", + "name": "Main", "request": "launch", - "desktop": true, + "mainClass": "frc.robot.Main", + "projectName": "Robot2026" }, { - "type": "wpilib", - "name": "WPILib roboRIO Debug", + "type": "java", + "name": "IndexerConstants", "request": "launch", - "desktop": false, - } - ] -} - -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - + "mainClass": "frc.robot.subsystems.indexer.IndexerConstants", + "projectName": "Robot2026" + }, + { + "type": "java", + "name": "Indexer", + "request": "launch", + "mainClass": "frc.robot.subsystems.indexer.Indexer", + "projectName": "Robot2026" + }, { "type": "wpilib", "name": "WPILib Desktop Debug", "request": "launch", - "desktop": true, + "desktop": true }, { "type": "wpilib", "name": "WPILib roboRIO Debug", "request": "launch", - "desktop": false, + "desktop": false } ] } diff --git a/build.gradle b/build.gradle index 06ca256..fff5f8f 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,13 @@ deploy { // getTargetTypeClass is a shortcut to get the class type using a string frcJava(getArtifactTypeClass('FRCJavaArtifact')) { + // Enable VisualVM connection + jvmArgs.add("-Dcom.sun.management.jmxremote=true") + jvmArgs.add("-Dcom.sun.management.jmxremote.port=1198") + jvmArgs.add("-Dcom.sun.management.jmxremote.local.only=false") + jvmArgs.add("-Dcom.sun.management.jmxremote.ssl=false") + jvmArgs.add("-Dcom.sun.management.jmxremote.authenticate=false") + jvmArgs.add("-Djava.rmi.server.hostname=10.14.58.2") // Replace TE.AM with team number } // Static files artifact @@ -52,11 +59,21 @@ def includeDesktopSupport = true // Defining my dependencies. In this case, WPILib (+ friends), and vendor libraries. // Also defines JUnit 5. + +task(replayWatch, type: JavaExec) { + mainClass = "org.littletonrobotics.junction.ReplayWatch" + classpath = sourceSets.main.runtimeClasspath +} + dependencies { annotationProcessor wpi.java.deps.wpilibAnnotations() implementation wpi.java.deps.wpilib() implementation wpi.java.vendor.java() + // ... + def akitJson = new groovy.json.JsonSlurper().parseText(new File(projectDir.getAbsolutePath() + "/vendordeps/AdvantageKit.json").text) + annotationProcessor "org.littletonrobotics.akit:akit-autolog:$akitJson.version" + roborioDebug wpi.java.deps.wpilibJniDebug(wpi.platforms.roborio) roborioDebug wpi.java.vendor.jniDebug(wpi.platforms.roborio) diff --git a/src/main/java/frc/robot/ControlsMapping.java b/src/main/java/frc/robot/ControlsMapping.java index e40c703..2ea272c 100644 --- a/src/main/java/frc/robot/ControlsMapping.java +++ b/src/main/java/frc/robot/ControlsMapping.java @@ -4,14 +4,23 @@ import com.ctre.phoenix6.swerve.SwerveRequest; -import edu.wpi.first.math.geometry.*; +import edu.wpi.first.math.geometry.Pose2d; +import edu.wpi.first.math.geometry.Rotation2d; +import edu.wpi.first.wpilibj.XboxController; import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Direction; import frc.robot.subsystems.drive.Drive; import frc.robot.subsystems.drive.ctre.CtreDrive.SysIdRoutineType; +import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.button.CommandXboxController; public class ControlsMapping { + CommandXboxController controller1; + public static void mapTeleopCommand() { + + + Drive.getInstance().setDefaultCommand((Drive.getInstance().teleopCommand())); // run sysID functions Drive.getInstance().getCtreDrive().setSysIdRoutine(SysIdRoutineType.STEER); @@ -21,7 +30,23 @@ public static void mapTeleopCommand() { controller.rightBumper().whileTrue(Drive.getInstance().autoAlign(false)); controller.x().whileTrue(Drive.getInstance().autopilotAlign(true)); controller.y().whileTrue(Drive.getInstance().autopilotAlign(false)); + controller.b().whileTrue(HangCommand()); // TODO: Implement hang from armaaan + controller.leftTrigger().whileTrue(intakeCommand()); + controller.rightTrigger().whileTrue(shooterCommand()); + } + + public static Command intakeCommand() { + return Commands.print("Intaking"); + } + public static Command HangCommand() { + return Commands.print("Hanging"); + } + public static Command shooterCommand() { + return Commands.print("Shooting"); + } + + //make a method that returns command, but it returns command.none, replace everything with new instance command with the command name. public static void mapSysId() { // set up sysID routine type @@ -65,4 +90,4 @@ public static void mapSysId() { } })); } -} +} \ No newline at end of file diff --git a/src/main/java/frc/robot/Robot.java b/src/main/java/frc/robot/Robot.java index 0b73ec1..50ebffd 100644 --- a/src/main/java/frc/robot/Robot.java +++ b/src/main/java/frc/robot/Robot.java @@ -4,6 +4,11 @@ import java.util.Optional; +import org.littletonrobotics.junction.LogFileUtil; +import org.littletonrobotics.junction.networktables.NT4Publisher; +import org.littletonrobotics.junction.wpilog.WPILOGReader; +import org.littletonrobotics.junction.wpilog.WPILOGWriter; + import com.pathplanner.lib.commands.FollowPathCommand; import edu.wpi.first.epilogue.Logged; diff --git a/src/main/java/frc/robot/subsystems/indexer/Indexer.java b/src/main/java/frc/robot/subsystems/indexer/Indexer.java new file mode 100644 index 0000000..2a094e8 --- /dev/null +++ b/src/main/java/frc/robot/subsystems/indexer/Indexer.java @@ -0,0 +1,179 @@ +package frc.robot.subsystems.indexer; + +import static frc.robot.subsystems.indexer.IndexerConstants.*; +import com.ctre.phoenix6.controls.ControlRequest; +import com.ctre.phoenix6.controls.VelocityVoltage; +import com.ctre.phoenix6.hardware.TalonFX; +import com.ctre.phoenix6.sim.TalonFXSimState; + +import edu.wpi.first.util.sendable.SendableBuilder; +import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; +import edu.wpi.first.wpilibj2.command.SubsystemBase; +import frc.robot.subsystems.TelemetryManager; +import au.grapplerobotics.LaserCan; +import au.grapplerobotics.ConfigurationFailedException; + +// TODO (ethan): only activate if shooter ready +// TODO (ethan): ask tommy setControl(request) +public class Indexer extends SubsystemBase { + /** getInstance of indexer */ + private static Indexer leftInstance; + public static Indexer getLeftInstance() { + if (leftInstance == null) { + leftInstance = new Indexer(true); + } + return leftInstance; + } + private static Indexer rightInstance; + public static Indexer getrightInstance() { + if (rightInstance == null) { + rightInstance = new Indexer(false); + } + return rightInstance; + } + private ControlRequest request; + + private TalonFX motor; + private TalonFXSimState motorSim; + private LaserCan lc; + + /** boolean that's modified by checkForBall() */ + private boolean hasBall; + + // private boolean shooterReady = false; + // COMMENTED OUT /** boolean that's modified by checkForBallTwo() */ + // private boolean hasBallTwo; + + /** boolean that's modified by shooter */ + + /** setup, adding motor and laser */ + private Indexer(boolean isLeft) { + super(); + setName("Indexer " + (isLeft ? "Left" : "Right")); + motor = new TalonFX(isLeft ? L_MOTOR_ID : R_MOTOR_ID); + motor.getConfigurator().apply(getConfig()); + motorSim = motor.getSimState(); + lc = new LaserCan(isLeft ? L_LASER_ID : R_LASER_ID); + // lcTwo = new LaserCan(LASER_ID_2); + + /* new laser configs */ + for (int i = 0; i < 20; i++) { + try { + lc.setRangingMode(LaserCan.RangingMode.SHORT); + lc.setRegionOfInterest(new LaserCan.RegionOfInterest(8, 8, 16, 16)); + lc.setTimingBudget(LaserCan.TimingBudget.TIMING_BUDGET_33MS); + break; + } catch (ConfigurationFailedException e) { + System.out.println("Configuration failed! " + e); + } + } + // try { + // lcTwo.setRangingMode(LaserCan.RangingMode.SHORT); + // lcTwo.setRegionOfInterest(new LaserCan.RegionOfInterest(8, 8, 16, 16)); + // lcTwo.setTimingBudget(LaserCan.TimingBudget.TIMING_BUDGET_33MS); + // } catch (ConfigurationFailedException e) { + // System.out.println("Configuration failed! " + e); + // } + setDefaultCommand(loadIndexer()); + } + + @Override + /* check for balls and makes sure motor is constantly running at desired speed */ + public void periodic() { + motor.setControl(request); + + checkForBall(); + // checkForBallTwo(); + // if (shooterReady == true) { + // activateIndexer(); + // } + } + + @Override + public void simulationPeriodic() { + + } + + /** type conversion/abstraction */ + public void setRequest(ControlRequest request) { + this.request = request; + } + + /** sets speed (duh) */ + public Command setSpeed(double speed) { + return runOnce(() -> setRequest(new VelocityVoltage(speed))); + } + + /** moves motor to speed if sense ball */ + public Command loadIndexer() { + return Commands.either( + deactivateIndexer(), + activateIndexer(), + this::hasBall + ).repeatedly(); + } + + public Command activateIndexer() { + return setSpeed(ROLLING_SPEED); + } + + /** turn motor down to zero */ + public Command deactivateIndexer() { + return setSpeed(0); + } + + /** command to sense distance from LaserCAN; used to sense if bol */ + private double getDistanceMm() { + LaserCan.Measurement measurement = lc.getMeasurement(); + if (measurement != null && measurement.status == LaserCan.LASERCAN_STATUS_VALID_MEASUREMENT) { + return measurement.distance_mm; + } else { + return Double.POSITIVE_INFINITY; + } + } + + /** command that modifies hasBall, uses getDistanceMm() */ + private void checkForBall() { + double x = getDistanceMm(); + if (x <= MAXIMUM_LASER_DIST) { + hasBall = false; + } + } + + /** setup formatting for boolean hasBall so it can be used in activateIndexer().either */ + private boolean hasBall() { + return hasBall; + } + // COMMENTED OUT /** setup formatting for boolean hasBall so that it can be used to tell if shooter ready */ + // private boolean hasBallTwo() { + // return hasBallTwo; + // } + // COMMENTED OUT /** command to sense if bol going into shooter */ + // private double getDistanceMmTwo() { + // LaserCan.Measurement measurement = lc.getMeasurement(); + // if (measurement != null && measurement.status == LaserCan.LASERCAN_STATUS_VALID_MEASUREMENT) { + // return measurement.distance_mm; + // } else { + // return -1; + // } + // } + // COMMENTED OUT //** command that checks if bol is boutta be shot */ + // private void checkForBallTwo() { + // double x = getDistanceMmTwo(); + // if (x >= LaserCan_DefaultMeasurement) { + // hasBallTwo = false; + // } else if (x != -1) { /* (x != -1) is checking that the camera isn't just returning an error as ball sensed */ + // hasBallTwo = true; + // } + // } + + // TODO: AdvantageKit! + /** ????????? */ + @Override + public void initSendable(SendableBuilder builder) { + super.initSendable(builder); + builder.addBooleanProperty("Has Ball", () -> hasBall, null); + TelemetryManager.makeSendableTalonFX("Indexer Motor", motor, builder); + } +} \ No newline at end of file diff --git a/src/main/java/frc/robot/subsystems/indexer/IndexerConstants.java b/src/main/java/frc/robot/subsystems/indexer/IndexerConstants.java new file mode 100644 index 0000000..b3e1e4a --- /dev/null +++ b/src/main/java/frc/robot/subsystems/indexer/IndexerConstants.java @@ -0,0 +1,38 @@ +package frc.robot.subsystems.indexer; + +import com.ctre.phoenix6.configs.CurrentLimitsConfigs; +import com.ctre.phoenix6.configs.Slot0Configs; +import com.ctre.phoenix6.configs.TalonFXConfiguration; +import com.ctre.phoenix6.configs.VoltageConfigs; + +public class IndexerConstants { + + // TODO: Set IDs + // TODO (Ethan): Set LaserCan_DefaultMeasurement + public static final int L_MOTOR_ID = 32; + public static final int L_LASER_ID = 36; + public static final int R_MOTOR_ID = 32; + public static final int R_LASER_ID = 36; + // public static final int LASER_ID_2 = 0; + public static final double ROLLING_SPEED = 10; // rps + public static final double MAXIMUM_LASER_DIST = 100; + + + public static TalonFXConfiguration getConfig() { + return new TalonFXConfiguration() + .withSlot0(new Slot0Configs() + .withKV(0.0) + .withKP(0.3) + .withKI(0.001) + .withKD(0.0) + .withKA(0.1) + .withKS(0.1) + .withKV(0.1)) // placeholder values + .withCurrentLimits(new CurrentLimitsConfigs() + .withStatorCurrentLimit(40) + .withSupplyCurrentLimit(40)) + .withVoltage(new VoltageConfigs() + .withPeakForwardVoltage(12.0) + .withPeakReverseVoltage(-12.0)); + } +} \ No newline at end of file diff --git a/vendordeps/libgrapplefrc2026.json b/vendordeps/libgrapplefrc2026.json new file mode 100644 index 0000000..2f620b5 --- /dev/null +++ b/vendordeps/libgrapplefrc2026.json @@ -0,0 +1,71 @@ +{ + "fileName": "libgrapplefrc2026.json", + "name": "libgrapplefrc", + "version": "2026.0.0", + "frcYear": "2026", + "uuid": "8ef3423d-9532-4665-8339-206dae1d7168", + "mavenUrls": [ + "https://storage.googleapis.com/grapple-frc-maven" + ], + "jsonUrl": "https://storage.googleapis.com/grapple-frc-maven/libgrapplefrc2026.json", + "javaDependencies": [ + { + "groupId": "au.grapplerobotics", + "artifactId": "libgrapplefrcjava", + "version": "2026.0.0" + } + ], + "jniDependencies": [ + { + "groupId": "au.grapplerobotics", + "artifactId": "libgrapplefrcdriver", + "version": "2026.0.0", + "skipInvalidPlatforms": true, + "isJar": false, + "validPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ], + "cppDependencies": [ + { + "groupId": "au.grapplerobotics", + "artifactId": "libgrapplefrccpp", + "version": "2026.0.0", + "libName": "grapplefrc", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + }, + { + "groupId": "au.grapplerobotics", + "artifactId": "libgrapplefrcdriver", + "version": "2026.0.0", + "libName": "grapplefrcdriver", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "windowsx86-64", + "linuxarm64", + "linuxx86-64", + "linuxathena", + "linuxarm32", + "osxuniversal" + ] + } + ] +} \ No newline at end of file