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();
+}