diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java index 9af5d3e1584e..d373401e6fd1 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java @@ -3,8 +3,11 @@ import com.qualcomm.robotcore.hardware.DcMotor; import com.qualcomm.robotcore.hardware.HardwareMap; +import org.firstinspires.ftc.teamcode.hardware.AutoClearEncoder; +import org.firstinspires.ftc.teamcode.hardware.HardwareMapper; import org.firstinspires.ftc.teamcode.hardware.HardwareName; import org.firstinspires.ftc.teamcode.hardware.Reversed; +import org.firstinspires.ftc.teamcode.hardware.ZeroPower; public class Hardware extends HardwareMapper { // left = left motor = exp 0 frontLeft @@ -13,43 +16,35 @@ public class Hardware extends HardwareMapper { @HardwareName("frontLeft") @Reversed + @ZeroPower(DcMotor.ZeroPowerBehavior.BRAKE) public DcMotor frontLeft; + @HardwareName("frontRight") + @ZeroPower(DcMotor.ZeroPowerBehavior.BRAKE) public DcMotor frontRight; + @HardwareName("backLeft") + @ZeroPower(DcMotor.ZeroPowerBehavior.BRAKE) @Reversed public DcMotor backLeft; + @HardwareName("backRight") + @ZeroPower(DcMotor.ZeroPowerBehavior.BRAKE) public DcMotor backRight; @HardwareName("frontLeft") + @AutoClearEncoder public DcMotor encoderLeft; + @HardwareName("intake") + @AutoClearEncoder public DcMotor encoderCenter; + @HardwareName("frontRight") + @AutoClearEncoder public DcMotor encoderRight; - private void resetEncoder(DcMotor target) { - DcMotor.RunMode mode = target.getMode(); - target.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER); - target.setMode(mode); - } - public Hardware(HardwareMap hwMap) { super(hwMap); - frontLeft = hwMap.dcMotor.get("frontLeft"); - frontRight = hwMap.dcMotor.get("frontRight"); - backLeft = hwMap.dcMotor.get("backLeft"); - backRight = hwMap.dcMotor.get("backRight"); - - encoderLeft = hwMap.dcMotor.get("frontLeft"); - encoderCenter = hwMap.dcMotor.get("intake"); - encoderRight = hwMap.dcMotor.get("frontRight"); - - frontLeft.setDirection(DcMotor.Direction.REVERSE); - backLeft.setDirection(DcMotor.Direction.REVERSE); - resetEncoder(encoderLeft); - resetEncoder(encoderCenter); - resetEncoder(encoderRight); } } diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/HardwareMapper.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/HardwareMapper.java deleted file mode 100644 index 22188c4fe05d..000000000000 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/HardwareMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.firstinspires.ftc.teamcode; - -import com.qualcomm.robotcore.hardware.HardwareMap; - -import org.firstinspires.ftc.teamcode.hardware.HardwareName; - -import java.lang.reflect.Field; - -public abstract class HardwareMapper { - public HardwareMapper(HardwareMap map) { - Field[] fields = this.getClass().getDeclaredFields(); - for (Field field : fields) { - boolean accessible = field.isAccessible(); - if (!accessible) field.setAccessible(true); - try { - Class targetType = field.getType(); - HardwareName annotation = field.getAnnotation(HardwareName.class); - if (annotation == null) continue; - field.set(this, map.get(targetType, field.getName())); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException("Field " + field.getName() + " typecast failed"); - } finally { - // lock the field back if it was locked - field.setAccessible(accessible); - } - } - } -} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/AutoClearEncoder.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/AutoClearEncoder.java new file mode 100644 index 000000000000..1d652da5ebc2 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/AutoClearEncoder.java @@ -0,0 +1,15 @@ +package org.firstinspires.ftc.teamcode.hardware; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies that an encoder should be cleared when the robot is initialized. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface AutoClearEncoder { } diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareMapper.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareMapper.java new file mode 100644 index 000000000000..e4a605b764b8 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareMapper.java @@ -0,0 +1,74 @@ +package org.firstinspires.ftc.teamcode.hardware; + +import com.qualcomm.robotcore.hardware.DcMotor; +import com.qualcomm.robotcore.hardware.DcMotorSimple; +import com.qualcomm.robotcore.hardware.HardwareMap; + +import java.lang.reflect.Field; + +/** + * Annotation processor for hardware map things. + */ +public abstract class HardwareMapper { + public HardwareMapper(HardwareMap map) { + Field[] fields = this.getClass().getDeclaredFields(); + for (Field field : fields) { + boolean accessible = field.isAccessible(); + if (!accessible) field.setAccessible(true); + try { + Class targetType = field.getType(); + HardwareName annotation = field.getAnnotation(HardwareName.class); + if (annotation == null) continue; + Object result = map.tryGet(targetType, field.getName()); + + if (result == null) { + throw new RuntimeException( + "Hardware: '" + + field.getName() + "' not found, expected type " + + targetType.getName() + " for field " + + field.getName() + " in " + this.getClass().getName() + ); + } + try { + field.set(this, result); + } catch (IllegalAccessException e) { + throw new RuntimeException("Field " + field.getName() + " assign failed: " + result.getClass().getName() + " to " + field.getType().getName()); + } + + if (field.isAnnotationPresent(Reversed.class)) { + if (targetType.isAssignableFrom(DcMotorSimple.class)) { + DcMotorSimple motor = (DcMotorSimple) result; + motor.setDirection(DcMotorSimple.Direction.REVERSE); + } else { + throw new RuntimeException("@Reversed annotation can only be used on DcMotorSimple (or subclasses)"); + } + } + if (field.isAnnotationPresent(ZeroPower.class)) { + if (targetType.isAssignableFrom(DcMotor.class)) { + DcMotor motor = (DcMotor) result; + ZeroPower zeroPower = field.getAnnotation(ZeroPower.class); + if (zeroPower == null) throw new RuntimeException("ZeroPower annotation is null?!"); + motor.setZeroPowerBehavior(zeroPower.value()); + } else { + throw new RuntimeException("@ZeroPower annotation can only be used on DcMotor (or subclasses)"); + } + } + if (field.isAnnotationPresent(AutoClearEncoder.class)) { + if (targetType.isAssignableFrom(DcMotor.class)) { + DcMotor motor = (DcMotor) result; + DcMotor.RunMode current = motor.getMode(); + motor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER); + motor.setMode(current); + } else { + throw new RuntimeException("@AutoClearEncoder annotation can only be used on DcMotor (or subclasses)"); + } + } + } catch (IllegalArgumentException e) { + throw new RuntimeException("Field " + field.getName() + " typecast failed"); + } finally { + // lock the field back if it was locked + field.setAccessible(accessible); + } + } + } +} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java index 6134c6ebd65a..88ae2a746663 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java @@ -1,5 +1,6 @@ package org.firstinspires.ftc.teamcode.hardware; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -7,6 +8,7 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) +@Documented public @interface HardwareName { String value(); } diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java index 123682038c0a..70ec46559ba2 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java @@ -1,10 +1,16 @@ package org.firstinspires.ftc.teamcode.hardware; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Specifies that a motor should start with its direction reversed. + * The annotated field must be a DcMotorSimple or subclass. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) +@Documented public @interface Reversed {} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/ZeroPower.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/ZeroPower.java new file mode 100644 index 000000000000..73372b953de9 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/ZeroPower.java @@ -0,0 +1,19 @@ +package org.firstinspires.ftc.teamcode.hardware; + +import com.qualcomm.robotcore.hardware.DcMotor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defiles the zero power behavior of a motor. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ZeroPower { + DcMotor.ZeroPowerBehavior value(); +} diff --git a/build.dependencies.gradle b/build.dependencies.gradle index 938d0c5bb821..86c7193c3ed1 100644 --- a/build.dependencies.gradle +++ b/build.dependencies.gradle @@ -1,7 +1,9 @@ repositories { mavenCentral() google() // Needed for androidx - + maven { + url = 'https://maven.brott.dev/' + } } dependencies { @@ -21,5 +23,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'com.acmerobotics.dashboard:dashboard:0.4.15' }