diff --git a/lib/src/main/java/com/team2813/lib2813/control/InvertType.java b/lib/src/main/java/com/team2813/lib2813/control/InvertType.java index ca6d398b..190ba954 100644 --- a/lib/src/main/java/com/team2813/lib2813/control/InvertType.java +++ b/lib/src/main/java/com/team2813/lib2813/control/InvertType.java @@ -15,14 +15,13 @@ */ package com.team2813.lib2813.control; -import static java.util.stream.Collectors.toUnmodifiableMap; - -import java.util.*; -import java.util.stream.Stream; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; public enum InvertType { - CLOCKWISE(true), - COUNTER_CLOCKWISE(false), + CLOCKWISE, + COUNTER_CLOCKWISE, FOLLOW_MASTER, OPPOSE_MASTER; @@ -32,44 +31,4 @@ public enum InvertType { */ public static final Set rotationValues = Collections.unmodifiableSet(EnumSet.of(CLOCKWISE, COUNTER_CLOCKWISE)); - - private final Optional sparkMaxInvert; - - InvertType() { - sparkMaxInvert = Optional.empty(); - } - - InvertType(boolean sparkMaxInvert) { - this.sparkMaxInvert = Optional.of(sparkMaxInvert); - } - - /** - * Gets an {@link InvertType} from a spark max invert - * - * @param v the value to search for - * @return {@link Optional#empty()} if no {@link InvertType} is found, otherwise, an optional - * describing the {@link InvertType} - */ - public static Optional fromSparkMaxInvert(boolean v) { - return Optional.of(Maps.sparkMaxMap.get(v)); - } - - public Optional sparkMaxInvert() { - return sparkMaxInvert; - } - - private boolean forceSparkMaxInvert() { - return sparkMaxInvert.orElseThrow(); - } - - /** - * Contains the maps for {@link InvertType#fromSparkMaxInvert(boolean)}. In a static class so that - * they will only be initialized if they are needed. - */ - private static final class Maps { - private static final Map sparkMaxMap = - Stream.of(InvertType.values()) - .filter((j) -> j.sparkMaxInvert.isPresent()) - .collect(toUnmodifiableMap(InvertType::forceSparkMaxInvert, (j) -> j, (a, b) -> null)); - } } diff --git a/lib/src/test/java/com/team2813/lib2813/control/InvertTypeTest.java b/lib/src/test/java/com/team2813/lib2813/control/InvertTypeTest.java deleted file mode 100644 index 5851ad64..00000000 --- a/lib/src/test/java/com/team2813/lib2813/control/InvertTypeTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2024-2026 Prospect Robotics SWENext Club - -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 - -http://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. -*/ -package com.team2813.lib2813.control; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class InvertTypeTest { - - @Test - public void sparkMaxInvertsExist() { - for (InvertType v : InvertType.rotationValues) { - assertTrue( - String.format("No spark max invert exists for InvertType %s.", v), - v.sparkMaxInvert().isPresent()); - } - } - - @Test - public void fromSparkMaxInvertTest() { - for (InvertType v : InvertType.rotationValues) { - boolean val = v.sparkMaxInvert().orElseThrow(); - assertEquals(v, InvertType.fromSparkMaxInvert(val).orElse(null)); - } - } -} diff --git a/settings.gradle b/settings.gradle index 356e580e..c2a8fd4f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,4 +9,4 @@ rootProject.name = 'lib2813' include 'lib', 'limelight', 'vision', 'testing' -include 'vendor:ctre' +include 'vendor:ctre', 'vendor:rev' diff --git a/vendor/rev/build.gradle b/vendor/rev/build.gradle new file mode 100644 index 00000000..c4648222 --- /dev/null +++ b/vendor/rev/build.gradle @@ -0,0 +1,45 @@ +plugins { + id 'java-common-conventions' + id 'publishing-conventions' + id 'edu.wpi.first.GradleRIO' version '2026.1.1' +} + +dependencies { + implementation wpi.java.deps.wpilib() + implementation wpi.java.vendor.java() + implementation project(':lib') + + 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(platform('org.junit:junit-bom:5.13.1')) + testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation('com.google.truth:truth:1.4.4') + testRuntimeOnly('org.junit.platform:junit-platform-launcher') +} + +// the magic line that makes tests work :) +wpi.java.configureTestTasks(test) + +tasks.named('test') { + useJUnitPlatform() +} + +mavenPublishing { + pom { + name = '2813 REV Robotics Vendor Library' + description = 'REV Robotics extensions to team2813:lib' + developers { + developer { + id = 'cuttestkittensrule' + name = 'Kyle' + email = 'mangoiscute95@gmail.com' + } + } + } +} diff --git a/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/InvertTypes.java b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/InvertTypes.java new file mode 100644 index 00000000..e1fb6162 --- /dev/null +++ b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/InvertTypes.java @@ -0,0 +1,47 @@ +/* +Copyright 2026 Prospect Robotics SWENext Club + +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 + +http://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. +*/ +package com.team2813.lib2813.vendor.rev; + +import com.team2813.lib2813.control.InvertType; +import java.util.Objects; +import java.util.Optional; + +/** Utility methods for working with {@link InvertType} for REV Robotics motors. */ +public class InvertTypes { + + /** + * Gets the SPARK MAX invert value for the given {@link InvertType} + * + * @return The invert, or {@link Optional#empty()} if the provided {@code invertType} is not a + * rotational value. + */ + public static Optional toSparkMaxInvert(InvertType invertType) { + return switch (Objects.requireNonNull(invertType, "invertType should not be null")) { + case CLOCKWISE -> Optional.of(Boolean.TRUE); + case COUNTER_CLOCKWISE -> Optional.of(Boolean.FALSE); + default -> Optional.empty(); + }; + } + + /** Gets the {@link InvertType} for the given SPARK MAX invert value. */ + public static InvertType toInvertType(boolean sparkMaxInvert) { + return sparkMaxInvert ? InvertType.CLOCKWISE : InvertType.COUNTER_CLOCKWISE; + } + + private InvertTypes() { + throw new AssertionError("Not instantiable"); + } +} diff --git a/lib/src/main/java/com/team2813/lib2813/util/ConfigUtils.java b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/RevUtils.java similarity index 86% rename from lib/src/main/java/com/team2813/lib2813/util/ConfigUtils.java rename to vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/RevUtils.java index 2e5e750d..be501217 100644 --- a/lib/src/main/java/com/team2813/lib2813/util/ConfigUtils.java +++ b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/RevUtils.java @@ -13,20 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.team2813.lib2813.util; +package com.team2813.lib2813.vendor.rev; import com.revrobotics.REVLibError; import edu.wpi.first.wpilibj.DriverStation; import java.util.function.Supplier; -public class ConfigUtils { +public class RevUtils { private static final int ATTEMPTS = 10; - // make class non-instantiable - private ConfigUtils() { - throw new AssertionError("cannot create ConfigUtils instance"); - } - public static void revConfig(Supplier configMethod) { REVLibError errorCode = configMethod.get(); for (int i = 1; i <= ATTEMPTS && errorCode != REVLibError.kOk; i++) { @@ -38,4 +33,8 @@ public static void revConfig(Supplier configMethod) { DriverStation.reportError(String.format("%s: Config Failed", errorCode.toString()), false); } } + + private RevUtils() { + throw new AssertionError("Not instantiable"); + } } diff --git a/lib/src/main/java/com/team2813/lib2813/control/motors/SparkMaxWrapper.java b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/motor/SparkMaxWrapper.java similarity index 82% rename from lib/src/main/java/com/team2813/lib2813/control/motors/SparkMaxWrapper.java rename to vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/motor/SparkMaxWrapper.java index 7da84a03..6d3f51c3 100644 --- a/lib/src/main/java/com/team2813/lib2813/control/motors/SparkMaxWrapper.java +++ b/vendor/rev/src/main/java/com/team2813/lib2813/vendor/rev/motor/SparkMaxWrapper.java @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package com.team2813.lib2813.control.motors; +package com.team2813.lib2813.vendor.rev.motor; import com.revrobotics.RelativeEncoder; import com.revrobotics.spark.ClosedLoopSlot; @@ -27,13 +27,15 @@ import com.team2813.lib2813.control.ControlMode; import com.team2813.lib2813.control.InvertType; import com.team2813.lib2813.control.PIDMotor; -import com.team2813.lib2813.util.ConfigUtils; +import com.team2813.lib2813.vendor.rev.InvertTypes; +import com.team2813.lib2813.vendor.rev.RevUtils; import edu.wpi.first.units.Units; import edu.wpi.first.units.measure.Angle; import edu.wpi.first.units.measure.AngularVelocity; import edu.wpi.first.units.measure.Current; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class SparkMaxWrapper implements PIDMotor { private final List followers = new ArrayList<>(); @@ -51,15 +53,15 @@ public class SparkMaxWrapper implements PIDMotor { * @param type The motor type connected to the controller. Brushless motor wires must be connected * to their matching colors and the hall sensor must be plugged in. Brushed motors must be * connected to the Red and Black terminals only. - * @param inverted Whether the motor is inverted + * @param invertType Whether the motor is inverted */ - public SparkMaxWrapper(int deviceId, SparkLowLevel.MotorType type, InvertType inverted) { + public SparkMaxWrapper(int deviceId, SparkLowLevel.MotorType type, InvertType invertType) { motor = new SparkMax(deviceId, type); - this.inverted = inverted.sparkMaxInvert().orElseThrow(); + this.inverted = toSparkMaxInvert(invertType); config = new SparkMaxConfig().apply(new AlternateEncoderConfig().inverted(this.inverted)); persistMode = SparkBase.PersistMode.kNoPersistParameters; resetMode = SparkBase.ResetMode.kResetSafeParameters; - ConfigUtils.revConfig(() -> motor.configure(config, resetMode, persistMode)); + RevUtils.revConfig(() -> motor.configure(config, resetMode, persistMode)); encoder = motor.getEncoder(); } @@ -116,7 +118,7 @@ public void configPIDF(int slot, double p, double i, double d, double f) { ClosedLoopConfig closedLoopConfig = new ClosedLoopConfig().pid(p, i, d, cSlot); closedLoopConfig.feedForward.kV(f, cSlot); config.apply(closedLoopConfig); - ConfigUtils.revConfig(() -> motor.configure(config, resetMode, persistMode)); + RevUtils.revConfig(() -> motor.configure(config, resetMode, persistMode)); } @Override @@ -134,18 +136,26 @@ public void configPID(double p, double i, double d) { configPIDF(0, p, i, d, 0); } - public void addFollower(int deviceId, SparkLowLevel.MotorType type, InvertType inverted) { + public void addFollower(int deviceId, SparkLowLevel.MotorType type, InvertType invertType) { SparkMax follower = new SparkMax(deviceId, type); boolean isInverted = - switch (inverted) { - case CLOCKWISE, COUNTER_CLOCKWISE -> inverted.sparkMaxInvert().orElseThrow(); + switch (invertType) { + case CLOCKWISE, COUNTER_CLOCKWISE -> toSparkMaxInvert(invertType); case FOLLOW_MASTER -> this.inverted; case OPPOSE_MASTER -> !this.inverted; }; - ConfigUtils.revConfig( + RevUtils.revConfig( () -> follower.configure( new SparkMaxConfig().follow(motor).inverted(isInverted), resetMode, persistMode)); followers.add(follower); // add to follower list so CANSparkMax follower object is preserved } + + private static boolean toSparkMaxInvert(InvertType invertType) { + Objects.requireNonNull(invertType, "invertType should not be null"); + return InvertTypes.toSparkMaxInvert(invertType) + .orElseThrow( + () -> + new IllegalArgumentException("invertType must be CLOCKWISE or COUNTER_CLOCKWISE")); + } } diff --git a/vendor/rev/src/test/java/com/team2813/lib2813/vendor/rev/InvertTypesTest.java b/vendor/rev/src/test/java/com/team2813/lib2813/vendor/rev/InvertTypesTest.java new file mode 100644 index 00000000..8da5d9d3 --- /dev/null +++ b/vendor/rev/src/test/java/com/team2813/lib2813/vendor/rev/InvertTypesTest.java @@ -0,0 +1,48 @@ +/* +Copyright 2026 Prospect Robotics SWENext Club + +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 + +http://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. +*/ +package com.team2813.lib2813.vendor.rev; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.team2813.lib2813.control.InvertType; +import java.util.Optional; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.FieldSource; +import org.junit.jupiter.params.provider.ValueSource; + +/** Tests for {@link InvertTypes}. */ +class InvertTypesTest { + + @ParameterizedTest + @FieldSource("com.team2813.lib2813.control.InvertType#rotationValues") + public void toSparkMaxInvert(InvertType invertType) { + Optional value = InvertTypes.toSparkMaxInvert(invertType); + + assertWithMessage("No InvertedValue exists for InvertType.%s", invertType) + .that(value) + .isPresent(); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void toInvertType(boolean sparkMaxInvert) { + InvertType invertType = InvertTypes.toInvertType(sparkMaxInvert); + + assertThat(invertType).isNotNull(); + assertThat(InvertTypes.toSparkMaxInvert(invertType)).hasValue(sparkMaxInvert); + } +} diff --git a/lib/vendordeps/REVLib.json b/vendor/rev/vendordeps/REVLib.json similarity index 100% rename from lib/vendordeps/REVLib.json rename to vendor/rev/vendordeps/REVLib.json