From e1b13069e5c74d81af254d5fe61874362c9fd898 Mon Sep 17 00:00:00 2001 From: penguinencounter <49845522+penguinencounter@users.noreply.github.com> Date: Sat, 27 Jul 2024 18:05:27 -0700 Subject: [PATCH] cherry pick hw --- .../firstinspires/ftc/teamcode/Hardware.java | 50 ++++++++ .../teamcode/hardware/AutoClearEncoder.java | 15 +++ .../ftc/teamcode/hardware/HardwareMapper.java | 111 ++++++++++++++++++ .../ftc/teamcode/hardware/HardwareName.java | 14 +++ .../ftc/teamcode/hardware/Reversed.java | 16 +++ .../ftc/teamcode/hardware/ZeroPower.java | 19 +++ 6 files changed, 225 insertions(+) create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/AutoClearEncoder.java create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareMapper.java create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java create mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/ZeroPower.java diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java new file mode 100644 index 000000000000..d373401e6fd1 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/Hardware.java @@ -0,0 +1,50 @@ +package org.firstinspires.ftc.teamcode; + +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 + // right = right motor = ctr 0 frontRight + // center = ctr 3 intake + + @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; + + public Hardware(HardwareMap hwMap) { + super(hwMap); + } +} 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..8467b51de7b3 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareMapper.java @@ -0,0 +1,111 @@ +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.annotation.Annotation; +import java.lang.reflect.Field; + +/** + * Annotation processor for hardware map things. + */ +public abstract class HardwareMapper { + /** + * Represents an annotation processor for hardware devices. + * Internals are a bit of a mess right now. + *

+ * HardwareName is hardcoded because it provides the value of the field. + */ + static final class DeviceAnnotation { + private final Class annotationClass; + private final Class targetClass; + private final Action action; + + @FunctionalInterface + interface Action { + void use(AnnotationT annotation, TargetType target); + } + + DeviceAnnotation(Class annotationClass, Class targetClass, Action action) { + this.annotationClass = annotationClass; + this.targetClass = targetClass; + this.action = action; + } + + private boolean check(Field t, Object value) { + if (!t.isAnnotationPresent(annotationClass)) return false; + AnnotationT annotation = t.getAnnotation(annotationClass); + if (annotation == null) + throw new NullPointerException("Annotation is null! (isAnnotationPresent is true but getAnnotation returned null)"); + if (!targetClass.isAssignableFrom(value.getClass())) + throw new ClassCastException("Annotation " + annotationClass.getName() + " can only be used on " + targetClass.getName() + " (or subclasses)"); + return true; + } + + void use(Field t, Object value) { + if (!check(t, value)) return; + AnnotationT annotation = t.getAnnotation(annotationClass); + if (annotation == null) + throw new NullPointerException("Annotation disappeared between check and use"); + action.use(annotation, targetClass.cast(value)); + } + } + + static final DeviceAnnotation reversed = + new DeviceAnnotation<>( + Reversed.class, + DcMotorSimple.class, + (annotation, target) -> target.setDirection(DcMotorSimple.Direction.REVERSE) + ); + + static final DeviceAnnotation zeroPower = + new DeviceAnnotation<>( + ZeroPower.class, + DcMotor.class, + (annotation, target) -> target.setZeroPowerBehavior(annotation.value()) + ); + + static final DeviceAnnotation autoClearEncoder = + new DeviceAnnotation<>( + AutoClearEncoder.class, + DcMotor.class, + (annotation, target) -> { + DcMotor.RunMode current = target.getMode(); + target.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER); + target.setMode(current); + } + ); + + 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, annotation.value()); + + 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()); + } + + reversed.use(field, result); + zeroPower.use(field, result); + autoClearEncoder.use(field, result); + } 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 new file mode 100644 index 000000000000..88ae2a746663 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/HardwareName.java @@ -0,0 +1,14 @@ +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; + +@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 new file mode 100644 index 000000000000..70ec46559ba2 --- /dev/null +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/hardware/Reversed.java @@ -0,0 +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(); +}