From 0cc04a2be9cf885218be27a0090b56deb0deb51f Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 29 Nov 2018 15:58:46 -0800 Subject: [PATCH 01/16] Migrate from ChickenTalon to IMotorController --- .../team1540/rooster/testers/BurnoutTester.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java b/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java index a21fb60e..f1f36810 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java @@ -1,5 +1,6 @@ package org.team1540.rooster.testers; +import com.ctre.phoenix.motorcontrol.IMotorController; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -8,14 +9,13 @@ import java.util.Optional; import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation; import org.apache.commons.math3.stat.descriptive.rank.Median; -import org.team1540.rooster.wrappers.ChickenTalon; /** * Reports motor burnouts by comparing the current draw across a series of similarly-purposed * motors and reporting low outliers. */ @SuppressWarnings("unused") -public class BurnoutTester extends AbstractTester implements Sendable { +public class BurnoutTester extends AbstractTester implements Sendable { private static final Median medianCalculator = new Median(); @@ -30,7 +30,7 @@ public class BurnoutTester extends AbstractTester impleme * * @param motorsToTest The motors to compare to each other. */ - public BurnoutTester(ChickenTalon... motorsToTest) { + public BurnoutTester(IMotorController... motorsToTest) { // Because passing in a reference to a non-static method in the constructor doesn't work. super((stupid) -> null, Arrays.asList(motorsToTest), Collections.singletonList((ignore) -> true), 150, 500); @@ -43,7 +43,7 @@ public BurnoutTester(ChickenTalon... motorsToTest) { * * @param motorsToTest The motors to compare to each other. */ - public BurnoutTester(List motorsToTest) { + public BurnoutTester(List motorsToTest) { // Because passing in a reference to a non-static method in the constructor doesn't work. super((stupid) -> null, motorsToTest, Collections.singletonList((ignore) -> true), 150, 500); @@ -57,7 +57,7 @@ public BurnoutTester(List motorsToTest) { * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public Boolean testBurnout(ChickenTalon manageable) { + public Boolean testBurnout(IMotorController manageable) { return manageable.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); } @@ -66,7 +66,8 @@ public Boolean testBurnout(ChickenTalon manageable) { */ @Override void periodic() { - double[] currents = itemsToTest.stream().mapToDouble(ChickenTalon::getOutputCurrent).toArray(); + double[] currents = itemsToTest.stream().mapToDouble(IMotorController::getOutputCurrent) + .toArray(); medianCurrent = medianCalculator.evaluate(currents); stdDevCurrent = stdDevCalculator.evaluate(currents); super.periodic(); @@ -98,7 +99,7 @@ public void setSubsystem(String subsystem) { */ @Override public void initSendable(SendableBuilder builder) { - for (ChickenTalon t : getItemsToTest()) { + for (IMotorController t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder builder.addBooleanProperty(t.getDeviceID() + "", () -> { // TODO probably cleaner version of this, at the least ifPresentOrElse() in Java 9 From 4be691dc793c5e17ec08e11d896339eb50d91292 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 29 Nov 2018 16:52:40 -0800 Subject: [PATCH 02/16] Create EncoderTester --- .../rooster/testers/EncoderTester.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java new file mode 100644 index 00000000..7acd7d11 --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java @@ -0,0 +1,140 @@ +package org.team1540.rooster.testers; + +import edu.wpi.first.wpilibj.Sendable; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.team1540.rooster.wrappers.ChickenTalon; + +/** + * Reports if an encoder appears to be non-functional by checking to see if a motor is running + * and if the corresponding encoder is moving. + */ +@SuppressWarnings("unused") +public class EncoderTester extends AbstractTester implements Sendable { + + private String name = "EncoderTester"; + + private double currentThreshold = 1; + private double velocityThreshold = 5; + + /** + * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 + * ms. Equivalent to {@link EncoderTester#EncoderTester(List) EncoderTester(Arrays.asList + * (motorsToTest))}. + * + * @param motorsToTest The {@link ChickenTalon ChickenTalons} to compare to each other. + */ + public EncoderTester(ChickenTalon... motorsToTest) { + this(Arrays.asList(motorsToTest)); + } + + /** + * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 ms. + * + * @param motorsToTest The {@link ChickenTalon ChickenTalons} to compare to each other. + */ + @SuppressWarnings("WeakerAccess") + public EncoderTester(List motorsToTest) { + // Because passing in a reference to a non-static method in the constructor doesn't work. + // Test will run if the motor is drawing over 1A of current. + super((stupid) -> null, motorsToTest, null, 150, 500); + this.setTest(this::testEncoder); + this.setRunConditions( + Collections.singletonList((motor) -> (motor).getOutputCurrent() > currentThreshold)); + } + + /** + * Tests to see if the encoder is working by checking to see if the controller is drawing more + * than 1 amp and if the selected {@link ChickenTalon} is moving at a velocity of less than 5. + * + * @param controller The {@link ChickenTalon} to test for burnout. + * @return Boolean indicating if the encoder is encoder has failed: true if it is not suspected + * of failure, false if it is suspected of failure. + */ + @SuppressWarnings("WeakerAccess") + public Boolean testEncoder(ChickenTalon controller) { + return !(controller.getOutputCurrent() > currentThreshold + && controller.getSelectedSensorVelocity() < velocityThreshold); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getSubsystem() { + return this.name; + } + + @Override + public void setSubsystem(String subsystem) { + this.name = subsystem; + } + + /** + * Gets the threshold under which a motor will not be tested for movement. Defaults to 1A. + * + * @return A double representing the current in amps. + */ + public double getCurrentThreshold() { + return currentThreshold; + } + + /** + * Sets the threshold under which a motor will not be tested for movement + * + * @param currentThreshold A double representing the current in amps. + */ + public void setCurrentThreshold(double currentThreshold) { + this.currentThreshold = currentThreshold; + } + + /** + * Gets the encoder velocity under which an encody failure will be reported. Defaults to 5. + * + * @return A double representing the velocity in whatever units are set. + */ + public double getVelocityThreshold() { + return velocityThreshold; + } + + /** + * Sets the encoder velocity under which an encody failure will be reported. + * + * @param velocityThreshold A double representing the velocity in whatever units are set. + */ + public void setVelocityThreshold(double velocityThreshold) { + this.velocityThreshold = velocityThreshold; + } + + /** + * Displays the current status of each {@link ChickenTalon}. + * + * @param builder The {@link SendableBuilder} to use. + */ + @Override + public void initSendable(SendableBuilder builder) { + for (ChickenTalon t : getItemsToTest()) { + // Get the most recent value if present, else simply don't add it to the builder + //noinspection Duplicates + builder.addBooleanProperty(t.getDeviceID() + "", () -> { + // TODO probably cleaner version of this, at the least ifPresentOrElse() in Java 9 + Optional> result = Optional.ofNullable(peekMostRecentResult(t)); + if (result.isPresent()) { + return result.get().getResult(); + } else { + return false; + } + }, null); + } + } +} From d901d8551e5f3c9d0b54603a32a3aa782413cffb Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 29 Nov 2018 16:54:56 -0800 Subject: [PATCH 03/16] Make runConditions nullable Default behavior is now to execute the tests regardless. Also updated corresponding documentation and comments --- .../rooster/testers/AbstractTester.java | 32 +++++++++++++------ .../rooster/testers/BurnoutTester.java | 25 +++++++-------- .../org/team1540/rooster/testers/Tester.java | 13 ++++++-- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java index b6562d35..732b5f1b 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java @@ -25,7 +25,7 @@ public abstract class AbstractTester implements Tester { List itemsToTest; @NotNull private Function test; - @NotNull + @Nullable private List> runConditions; @NotNull private Map> storedResults; @@ -39,7 +39,7 @@ public abstract class AbstractTester implements Tester { * item. */ AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @NotNull List> runConditions) { + @Nullable List> runConditions) { this(test, itemsToTest, runConditions, (int) DEFAULT_LOG_TIME / (DEFAULT_UPDATE_DELAY / 1000)); } @@ -56,7 +56,7 @@ public abstract class AbstractTester implements Tester { * @param updateDelay The delay between the test being run on the items. */ AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @NotNull List> runConditions, float logTime, int updateDelay) { + @Nullable List> runConditions, float logTime, int updateDelay) { this(test, itemsToTest, runConditions, (int) (logTime / ((float) updateDelay / 1000f))); this.updateDelay = updateDelay; @@ -71,8 +71,9 @@ public abstract class AbstractTester implements Tester { * item. * @param queueDepth The maximum number of items that the {@link EvictingQueue} can hold. */ + @SuppressWarnings("WeakerAccess") AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @NotNull List> runConditions, int queueDepth) { + @Nullable List> runConditions, int queueDepth) { this.test = test; this.itemsToTest = itemsToTest; this.runConditions = runConditions; @@ -95,11 +96,18 @@ public void setTest(@NotNull Function test) { } @Override - @NotNull + @Nullable public List> getRunConditions() { - return Collections.unmodifiableList(runConditions); + return runConditions; + } + + @Override + public void setRunConditions( + @Nullable List> runConditions) { + this.runConditions = runConditions; } + @Override @NotNull public List getItemsToTest() { @@ -143,12 +151,16 @@ public boolean setRunning(boolean status) { */ void periodic() { for (T t : itemsToTest) { - // Run through all the run conditions and make sure they all return true - for (Function runCondition : runConditions) { - if (!runCondition.apply(t)) { - return; + // If there are run conditions + if (runConditions != null) { + // Run through all the run conditions and make sure they all return true + for (Function runCondition : runConditions) { + if (!runCondition.apply(t)) { + return; + } } } + this.storedResults.get(t).addResult(this.test.apply(t), System.currentTimeMillis()); } } diff --git a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java b/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java index f1f36810..2d3a72f7 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java @@ -4,7 +4,6 @@ import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Optional; import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation; @@ -26,39 +25,38 @@ public class BurnoutTester extends AbstractTester imp private String name = "BurnoutTester"; /** - * Construct a new instance. + * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 + * ms. Equivalent to {@link BurnoutTester#BurnoutTester(List) EncoderTester(Arrays.asList + * (motorsToTest))}. * * @param motorsToTest The motors to compare to each other. */ + @SuppressWarnings("WeakerAccess") public BurnoutTester(IMotorController... motorsToTest) { - // Because passing in a reference to a non-static method in the constructor doesn't work. - super((stupid) -> null, Arrays.asList(motorsToTest), - Collections.singletonList((ignore) -> true), 150, 500); - this.setTest(this::testBurnout); - this.setUpdateDelay(500); + this(Arrays.asList(motorsToTest)); } /** - * Construct a new instance. + * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 ms. * * @param motorsToTest The motors to compare to each other. */ + @SuppressWarnings("WeakerAccess") public BurnoutTester(List motorsToTest) { // Because passing in a reference to a non-static method in the constructor doesn't work. - super((stupid) -> null, motorsToTest, - Collections.singletonList((ignore) -> true), 150, 500); + super((stupid) -> null, motorsToTest, null, 150, 500); this.setTest(this::testBurnout); } /** * Tests to see if a motor is burned out by checking to see if it is at least one standard * deviation below the median. - * @param manageable The motor to test for burnout. + * @param controller The motor to test for burnout. * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public Boolean testBurnout(IMotorController manageable) { - return manageable.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); + public Boolean testBurnout(IMotorController controller) { + return controller.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); } /** @@ -101,6 +99,7 @@ public void setSubsystem(String subsystem) { public void initSendable(SendableBuilder builder) { for (IMotorController t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder + //noinspection Duplicates builder.addBooleanProperty(t.getDeviceID() + "", () -> { // TODO probably cleaner version of this, at the least ifPresentOrElse() in Java 9 Optional> result = Optional.ofNullable(peekMostRecentResult(t)); diff --git a/lib/src/main/java/org/team1540/rooster/testers/Tester.java b/lib/src/main/java/org/team1540/rooster/testers/Tester.java index f2efa4da..51e98dae 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/Tester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/Tester.java @@ -23,12 +23,21 @@ public interface Tester extends Runnable { /** * Gets the conditions that must be met before the test will be executed on an item. * - * @return An {@link EvictingQueue} of the run conditions. + * @return An potentially {@link List} of the run conditions, or null if there are none (always + * execute.) */ @SuppressWarnings("UnstableApiUsage") - @NotNull // TODO how to annotate as unmodifiable? + @Nullable List> getRunConditions(); + /** + * Sets the run conditions that must be met before the test will be executed on an item. + * + * @param runConditions A {@link List} of the run conditions, or null if the test should always + * run. + */ + void setRunConditions(@Nullable List> runConditions); + /** * Gets the items that the tests are being applied to. * From 128ad84efa85b61b238fadd854ec627f8683d935 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 3 Dec 2018 16:07:55 -0800 Subject: [PATCH 04/16] Add stateless4j dependency --- lib/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/build.gradle b/lib/build.gradle index 8b81dcb5..d7d2ef6a 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -45,6 +45,6 @@ dependencies { compile "jaci.pathfinder:Pathfinder-Java:1.8" compile "openrio.powerup:MatchData:2018.01.07" compile "com.google.guava:guava:27.0-jre" - // https://mvnrepository.com/artifact/org.apache.commons/commons-math3 - compile 'org.apache.commons:commons-math3:3.6.1' + compile "org.apache.commons:commons-math3:3.6.1" + compile "com.github.oxo42:stateless4j:2.5.0" } From fda86a4a1d20fb75ce5fd71e75a8d5a7c66303f5 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 3 Dec 2018 16:10:13 -0800 Subject: [PATCH 05/16] Migrate motor testers to separate package --- .../org/team1540/rooster/testers/AbstractTester.java | 10 +++++----- .../rooster/testers/{ => motor}/BurnoutTester.java | 5 +++-- .../rooster/testers/{ => motor}/EncoderTester.java | 5 +++-- .../testers/{ => motor}/SimpleControllersTester.java | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) rename lib/src/main/java/org/team1540/rooster/testers/{ => motor}/BurnoutTester.java (96%) rename lib/src/main/java/org/team1540/rooster/testers/{ => motor}/EncoderTester.java (96%) rename lib/src/main/java/org/team1540/rooster/testers/{ => motor}/SimpleControllersTester.java (99%) diff --git a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java index 732b5f1b..56eabb27 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java @@ -22,7 +22,7 @@ public abstract class AbstractTester implements Tester { private int updateDelay; private boolean running = true; @NotNull - List itemsToTest; + private List itemsToTest; @NotNull private Function test; @Nullable @@ -38,7 +38,7 @@ public abstract class AbstractTester implements Tester { * @param runConditions The conditions that must be met before the test will be executed on an * item. */ - AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, @Nullable List> runConditions) { this(test, itemsToTest, runConditions, (int) DEFAULT_LOG_TIME / (DEFAULT_UPDATE_DELAY / 1000)); } @@ -55,7 +55,7 @@ public abstract class AbstractTester implements Tester { * checked against while running. * @param updateDelay The delay between the test being run on the items. */ - AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, @Nullable List> runConditions, float logTime, int updateDelay) { this(test, itemsToTest, runConditions, (int) (logTime / ((float) updateDelay / 1000f))); @@ -72,7 +72,7 @@ public abstract class AbstractTester implements Tester { * @param queueDepth The maximum number of items that the {@link EvictingQueue} can hold. */ @SuppressWarnings("WeakerAccess") - AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, @Nullable List> runConditions, int queueDepth) { this.test = test; this.itemsToTest = itemsToTest; @@ -149,7 +149,7 @@ public boolean setRunning(boolean status) { * The code that should be called every tick. This does the actual testing. Override me as * necessary (but don't forget to call super!) */ - void periodic() { + protected void periodic() { for (T t : itemsToTest) { // If there are run conditions if (runConditions != null) { diff --git a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java similarity index 96% rename from lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java rename to lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java index 2d3a72f7..db49e0c2 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/BurnoutTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -1,4 +1,4 @@ -package org.team1540.rooster.testers; +package org.team1540.rooster.testers.motor; import com.ctre.phoenix.motorcontrol.IMotorController; import edu.wpi.first.wpilibj.Sendable; @@ -8,6 +8,8 @@ import java.util.Optional; import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation; import org.apache.commons.math3.stat.descriptive.rank.Median; +import org.team1540.rooster.testers.AbstractTester; +import org.team1540.rooster.testers.ResultWithMetadata; /** * Reports motor burnouts by comparing the current draw across a series of similarly-purposed @@ -16,7 +18,6 @@ @SuppressWarnings("unused") public class BurnoutTester extends AbstractTester implements Sendable { - private static final Median medianCalculator = new Median(); private static final StandardDeviation stdDevCalculator = new StandardDeviation(); private double medianCurrent = 0; diff --git a/lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java similarity index 96% rename from lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java rename to lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index 7acd7d11..b5a8be41 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/EncoderTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -1,4 +1,4 @@ -package org.team1540.rooster.testers; +package org.team1540.rooster.testers.motor; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; @@ -6,7 +6,8 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import org.team1540.rooster.wrappers.ChickenTalon; +import org.team1540.rooster.testers.AbstractTester; +import org.team1540.rooster.testers.ResultWithMetadata; /** * Reports if an encoder appears to be non-functional by checking to see if a motor is running diff --git a/lib/src/main/java/org/team1540/rooster/testers/SimpleControllersTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java similarity index 99% rename from lib/src/main/java/org/team1540/rooster/testers/SimpleControllersTester.java rename to lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java index e01226f7..dd27995f 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/SimpleControllersTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java @@ -1,4 +1,4 @@ -package org.team1540.rooster.testers; +package org.team1540.rooster.testers.motor; import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix.motorcontrol.IMotorController; From 1237c4506d485e2e8e4de1b58f02dcda78a92445 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 3 Dec 2018 16:11:16 -0800 Subject: [PATCH 06/16] Add better support for single motor testing Can now check using cutoffs instead of comparing against other motors in the group. Also includes accessors for the relevant fields. --- .../rooster/testers/motor/BurnoutTester.java | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java index db49e0c2..6f324666 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -13,7 +13,7 @@ /** * Reports motor burnouts by comparing the current draw across a series of similarly-purposed - * motors and reporting low outliers. + * motors and reporting low outliers or checking if a single motor is below the cutoff. */ @SuppressWarnings("unused") public class BurnoutTester extends AbstractTester implements Sendable { @@ -23,12 +23,17 @@ public class BurnoutTester extends AbstractTester imp private double medianCurrent = 0; private double stdDevCurrent = 0; + private double currentCutoff = 1; + private double percentOutputCutoff = 0.5; + private String name = "BurnoutTester"; /** * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 - * ms. Equivalent to {@link BurnoutTester#BurnoutTester(List) EncoderTester(Arrays.asList - * (motorsToTest))}. + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(IMotorController)} if there is more + * than one motor and {@link BurnoutTester#testBurnoutSingleMotor(IMotorController)} if there is + * one or few motors. Equivalent to {@link BurnoutTester#BurnoutTester(List) EncoderTester + * (Arrays.asList(motorsToTest))}. * * @param motorsToTest The motors to compare to each other. */ @@ -38,7 +43,10 @@ public BurnoutTester(IMotorController... motorsToTest) { } /** - * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 ms. + * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(IMotorController)} if there are more + * than two motor sand {@link BurnoutTester#testBurnoutSingleMotor(IMotorController)} if there two + * two or few motors. * * @param motorsToTest The motors to compare to each other. */ @@ -46,26 +54,40 @@ public BurnoutTester(IMotorController... motorsToTest) { public BurnoutTester(List motorsToTest) { // Because passing in a reference to a non-static method in the constructor doesn't work. super((stupid) -> null, motorsToTest, null, 150, 500); - this.setTest(this::testBurnout); + this.setTest(motorsToTest.size() > 2 ? this::testBurnoutMultiMotor : + this::testBurnoutSingleMotor); } /** - * Tests to see if a motor is burned out by checking to see if it is at least one standard - * deviation below the median. + * Tests to see if a motor is burned out by checking to see if the current being drawn is at + * least one standard deviation below the median. * @param controller The motor to test for burnout. * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public Boolean testBurnout(IMotorController controller) { + public boolean testBurnoutMultiMotor(IMotorController controller) { return controller.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); } + /** + * Test to see if a motor is burned out by checking to see if the current being drawn is below + * the current cutoff. + * + * @param controller The motor to test for burnout. + * @return Boolean indicating burnout. + */ + @SuppressWarnings("WeakerAccess") + public boolean testBurnoutSingleMotor(IMotorController controller) { + return controller.getMotorOutputPercent() > percentOutputCutoff + && controller.getOutputCurrent() < currentCutoff; + } + /** * Gets the currents, calculates the median and standard deviation, then calls super. */ @Override - void periodic() { - double[] currents = itemsToTest.stream().mapToDouble(IMotorController::getOutputCurrent) + protected void periodic() { + double[] currents = getItemsToTest().stream().mapToDouble(IMotorController::getOutputCurrent) .toArray(); medianCurrent = medianCalculator.evaluate(currents); stdDevCurrent = stdDevCalculator.evaluate(currents); @@ -92,6 +114,42 @@ public void setSubsystem(String subsystem) { this.name = subsystem; } + /** + * Gets the current cutoff. Defaults to 1A. + * + * @return The current cutoff in amps. + */ + public double getCurrentCutoff() { + return currentCutoff; + } + + /** + * Sets the current cutoff. + * + * @param currentCutoff The current cutoff in amps. + */ + public void setCurrentCutoff(double currentCutoff) { + this.currentCutoff = currentCutoff; + } + + /** + * Gets the percent output cutoff. Defaults to 50%. + * + * @return The percent output cutoff as a percentage. + */ + public double getPercentOutputCutoff() { + return percentOutputCutoff; + } + + /** + * Sets the percent output cutoff. + * + * @param percentOutputCutoff The output cutoff as a percentage. + */ + public void setPercentOutputCutoff(double percentOutputCutoff) { + this.percentOutputCutoff = percentOutputCutoff; + } + /** * Displays the current status of each motor and the median current draw. * @param builder The {@link SendableBuilder} to use. From 922ffe42899a18a06998c227d6c50460c356720e Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 3 Dec 2018 16:12:03 -0800 Subject: [PATCH 07/16] Refactor IMotorControllers to ChickenTalons Does this lose a little bit of functionality? Yes. Does it also make things otherwise massively nicer all around? Also yes. --- .idea/kotlinc.xml | 5 ++++ gradle/wrapper/gradle-wrapper.properties | 3 ++- .../rooster/testers/motor/EncoderTester.java | 24 ++++++++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 8b7f4afd..3b930d30 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -3,4 +3,9 @@ + + + \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 933b6473..1510c29a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Fri Nov 30 17:46:10 PST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index b5a8be41..f983c697 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -1,5 +1,6 @@ package org.team1540.rooster.testers.motor; +import com.ctre.phoenix.motorcontrol.IMotorController; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -14,7 +15,7 @@ * and if the corresponding encoder is moving. */ @SuppressWarnings("unused") -public class EncoderTester extends AbstractTester implements Sendable { +public class EncoderTester extends AbstractTester implements Sendable { private String name = "EncoderTester"; @@ -26,19 +27,19 @@ public class EncoderTester extends AbstractTester impleme * ms. Equivalent to {@link EncoderTester#EncoderTester(List) EncoderTester(Arrays.asList * (motorsToTest))}. * - * @param motorsToTest The {@link ChickenTalon ChickenTalons} to compare to each other. + * @param motorsToTest The {@link IMotorController IMotorControllers} to compare to each other. */ - public EncoderTester(ChickenTalon... motorsToTest) { + public EncoderTester(IMotorController... motorsToTest) { this(Arrays.asList(motorsToTest)); } /** * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 ms. * - * @param motorsToTest The {@link ChickenTalon ChickenTalons} to compare to each other. + * @param motorsToTest The {@link IMotorController IMotorControllers} to compare to each other. */ @SuppressWarnings("WeakerAccess") - public EncoderTester(List motorsToTest) { + public EncoderTester(List motorsToTest) { // Because passing in a reference to a non-static method in the constructor doesn't work. // Test will run if the motor is drawing over 1A of current. super((stupid) -> null, motorsToTest, null, 150, 500); @@ -49,16 +50,17 @@ public EncoderTester(List motorsToTest) { /** * Tests to see if the encoder is working by checking to see if the controller is drawing more - * than 1 amp and if the selected {@link ChickenTalon} is moving at a velocity of less than 5. + * than 1 amp and if the selected {@link IMotorController} is moving at a velocity of less than 5. * - * @param controller The {@link ChickenTalon} to test for burnout. + * @param controller The {@link IMotorController} to test for burnout. * @return Boolean indicating if the encoder is encoder has failed: true if it is not suspected * of failure, false if it is suspected of failure. */ @SuppressWarnings("WeakerAccess") - public Boolean testEncoder(ChickenTalon controller) { + public Boolean testEncoder(IMotorController controller) { + // Do the wrappers provide pidIdx nicely? Yes. Can we just use zero? Also probably yes. return !(controller.getOutputCurrent() > currentThreshold - && controller.getSelectedSensorVelocity() < velocityThreshold); + && controller.getSelectedSensorVelocity(0) < velocityThreshold); } @Override @@ -118,13 +120,13 @@ public void setVelocityThreshold(double velocityThreshold) { } /** - * Displays the current status of each {@link ChickenTalon}. + * Displays the current status of each {@link IMotorController}. * * @param builder The {@link SendableBuilder} to use. */ @Override public void initSendable(SendableBuilder builder) { - for (ChickenTalon t : getItemsToTest()) { + for (IMotorController t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder //noinspection Duplicates builder.addBooleanProperty(t.getDeviceID() + "", () -> { From d1f23b491c7fec8216c5b23e401a3b23d2b56374 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 3 Dec 2018 16:13:03 -0800 Subject: [PATCH 08/16] Create multiple tester encapsulation Automated thing to run a bunch of tests on motors. Doesn't crash and appears to report reasonable results but hasn't been fully tested or documented. --- .../testers/motor/ControllersMultiTester.java | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java new file mode 100644 index 00000000..fce5b897 --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -0,0 +1,120 @@ +package org.team1540.rooster.testers.motor; + +import com.ctre.phoenix.motorcontrol.ControlMode; +import com.ctre.phoenix.motorcontrol.IMotorController; +import com.github.oxo42.stateless4j.StateMachine; +import com.github.oxo42.stateless4j.StateMachineConfig; +import edu.wpi.first.wpilibj.DriverStation; +import edu.wpi.first.wpilibj.Timer; +import edu.wpi.first.wpilibj.command.Command; +import java.util.LinkedList; +import java.util.List; +import org.team1540.rooster.testers.AbstractTester; +import org.team1540.rooster.testers.ResultWithMetadata; +import org.team1540.rooster.wrappers.ChickenTalon; + +public class ControllersMultiTester extends Command { + + private Timer timer = new Timer(); + private StateMachine stateMachine; + private List> tests = new LinkedList<>(); + private int index = 0; + + public ControllersMultiTester() { + StateMachineConfig stateMachineConfig = new StateMachineConfig<>(); + stateMachineConfig.configure(State.SPIN_UP).permit(Trigger.TIME_HAS_PASSED, State.EXECUTING) + .onEntry(() -> { + for (IMotorController motor : tests.get(index).getItemsToTest()) { + motor.set(ControlMode.PercentOutput, 1); + } + }); + stateMachineConfig.configure(State.EXECUTING).permit(Trigger.TIME_HAS_PASSED, + State.SPIN_DOWN).onEntry(() -> new Thread(tests.get(0)).start()) + .onExit(() -> tests.get(0).setRunning(false)); + stateMachineConfig.configure(State.SPIN_DOWN).permit(Trigger.TIME_HAS_PASSED, State.SPIN_UP) + .permit(Trigger.FINISHED, State.FINISHED).onEntry(() -> { + for (IMotorController motor : tests.get(index).getItemsToTest()) { + motor.set(ControlMode.PercentOutput, 0); + } + index++; + }); + this.stateMachine = new StateMachine<>(State.SPIN_UP, stateMachineConfig); + } + + public ControllersMultiTester addControllerGroup(IMotorController... controllerGroup) { + tests.add(new BurnoutTester(controllerGroup)); + return this; + } + + public ControllersMultiTester addEncoderGroup(ChickenTalon... controllerGroup) { + tests.add(new EncoderTester(controllerGroup)); + return this; + } + + @Override + protected void initialize() { + timer.start(); + } + + @Override + protected void execute() { + if (stateMachine.getState().timeToComplete != null) { + if (timer.hasPeriodPassed(stateMachine.getState().getTimeToComplete())) { + if (index >= tests.size()) { + stateMachine.fire(Trigger.FINISHED); + } else { + stateMachine.fire(Trigger.TIME_HAS_PASSED); + } + } + } + } + + @Override + protected void end() { + // Very optimized yes no duplicate code either + for (AbstractTester tester : tests) { + for (IMotorController controller : tester.getItemsToTest()) { + int failureCount = 0; + for (ResultWithMetadata result : tester.getStoredResults(controller)) { + if (result.getResult().equals(Boolean.TRUE)) { + failureCount++; + } + } + if (failureCount > 0) { + DriverStation + .reportError("Motor " + controller.getDeviceID() + " reported " + failureCount + + " failures of type " + tester, false); + } + } + } + System.out.println("Finished testing"); + } + + @Override + protected boolean isFinished() { + return stateMachine.getState().equals(State.FINISHED); + } + + private enum State { + SPIN_UP(0.25), EXECUTING(3), SPIN_DOWN(0.25), FINISHED; + + private final Double timeToComplete; + + State() { + this.timeToComplete = null; + } + + State(double timeToComplete) { + this.timeToComplete = timeToComplete; + } + + public Double getTimeToComplete() { + return timeToComplete; + } + } + + private enum Trigger { + TIME_HAS_PASSED, FINISHED + } + +} From 36ae64fdf7c0a282086e96359bd7c7ddb1ab1a03 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:38:09 -0800 Subject: [PATCH 09/16] Disable braking on run by default --- .../rooster/testers/motor/SimpleControllersTester.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java index dd27995f..95cc63d0 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java @@ -2,6 +2,7 @@ import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix.motorcontrol.IMotorController; +import com.ctre.phoenix.motorcontrol.NeutralMode; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.buttons.JoystickButton; @@ -86,11 +87,11 @@ public SimpleControllersTester(Joystick joystick, int axisId, int nextButtonId, this.controllers.put(controller.getDeviceID(), controller); this.controllerChooser.addObject(String.valueOf(controller.getDeviceID()), controller.getDeviceID()); + controller.setNeutralMode(NeutralMode.Coast); } // Set the active controller to the first controller setCurrentController(controllers[0].getDeviceID()); - } /** From 44917873f1093f21bb5262c1052c5a758fd3a419 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:39:26 -0800 Subject: [PATCH 10/16] Suppress duplicate inspection Could these two probably be merged into some generalized motor thing? Probably. Unnecessary abstraction? Likely. --- .../java/org/team1540/rooster/testers/motor/BurnoutTester.java | 2 +- .../java/org/team1540/rooster/testers/motor/EncoderTester.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java index 6f324666..9ae39f9d 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -156,9 +156,9 @@ public void setPercentOutputCutoff(double percentOutputCutoff) { */ @Override public void initSendable(SendableBuilder builder) { + //noinspection Duplicates for (IMotorController t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder - //noinspection Duplicates builder.addBooleanProperty(t.getDeviceID() + "", () -> { // TODO probably cleaner version of this, at the least ifPresentOrElse() in Java 9 Optional> result = Optional.ofNullable(peekMostRecentResult(t)); diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index f983c697..7da77434 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -126,6 +126,7 @@ public void setVelocityThreshold(double velocityThreshold) { */ @Override public void initSendable(SendableBuilder builder) { + //noinspection Duplicates for (IMotorController t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder //noinspection Duplicates From a7673a13f4b08c18c323a63691eb3fe6912aed82 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:41:34 -0800 Subject: [PATCH 11/16] Minor math fixes Add missing abs Remove wrong negation and update documentation to match --- .../org/team1540/rooster/testers/motor/EncoderTester.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index 7da77434..bd8d0277 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -53,14 +53,14 @@ public EncoderTester(List motorsToTest) { * than 1 amp and if the selected {@link IMotorController} is moving at a velocity of less than 5. * * @param controller The {@link IMotorController} to test for burnout. - * @return Boolean indicating if the encoder is encoder has failed: true if it is not suspected - * of failure, false if it is suspected of failure. + * @return Boolean indicating if the encoder is encoder has failed: true if it is suspected + * of failure, false if it is not suspected of failure. */ @SuppressWarnings("WeakerAccess") public Boolean testEncoder(IMotorController controller) { // Do the wrappers provide pidIdx nicely? Yes. Can we just use zero? Also probably yes. - return !(controller.getOutputCurrent() > currentThreshold - && controller.getSelectedSensorVelocity(0) < velocityThreshold); + return controller.getOutputCurrent() > currentThreshold + && Math.abs(controller.getSelectedSensorVelocity(0)) < velocityThreshold; } @Override From 9ef5efdd83484a77749e0b7dbaab8748d7e9aaae Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:41:55 -0800 Subject: [PATCH 12/16] Suppress warning --- .../java/org/team1540/rooster/testers/motor/EncoderTester.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index bd8d0277..9a2b2559 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -29,6 +29,7 @@ public class EncoderTester extends AbstractTester imp * * @param motorsToTest The {@link IMotorController IMotorControllers} to compare to each other. */ + @SuppressWarnings("WeakerAccess") public EncoderTester(IMotorController... motorsToTest) { this(Arrays.asList(motorsToTest)); } From 9f810366ca7e44c369a226eaddcf9f9d68683d3f Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:42:44 -0800 Subject: [PATCH 13/16] Encapsulate tests with command Now you can set a special command, or just use the default set to full. --- .../testers/motor/ControllersMultiTester.java | 94 ++++++++++++++----- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java index fce5b897..3616a043 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -2,6 +2,7 @@ import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix.motorcontrol.IMotorController; +import com.ctre.phoenix.motorcontrol.NeutralMode; import com.github.oxo42.stateless4j.StateMachine; import com.github.oxo42.stateless4j.StateMachineConfig; import edu.wpi.first.wpilibj.DriverStation; @@ -9,6 +10,7 @@ import edu.wpi.first.wpilibj.command.Command; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; import org.team1540.rooster.testers.AbstractTester; import org.team1540.rooster.testers.ResultWithMetadata; import org.team1540.rooster.wrappers.ChickenTalon; @@ -17,42 +19,63 @@ public class ControllersMultiTester extends Command { private Timer timer = new Timer(); private StateMachine stateMachine; - private List> tests = new LinkedList<>(); + private List tests = new LinkedList<>(); private int index = 0; public ControllersMultiTester() { StateMachineConfig stateMachineConfig = new StateMachineConfig<>(); - stateMachineConfig.configure(State.SPIN_UP).permit(Trigger.TIME_HAS_PASSED, State.EXECUTING) + stateMachineConfig.configure(State.INITIALIZING) + .permit(Trigger.TIME_HAS_PASSED, State.SPIN_UP); + stateMachineConfig.configure(State.SPIN_UP) + .permit(Trigger.TIME_HAS_PASSED, State.EXECUTING) .onEntry(() -> { - for (IMotorController motor : tests.get(index).getItemsToTest()) { - motor.set(ControlMode.PercentOutput, 1); + for (IMotorController motor : tests.get(index).getTest().getItemsToTest()) { + tests.get(index).getFunction().accept(motor); } }); - stateMachineConfig.configure(State.EXECUTING).permit(Trigger.TIME_HAS_PASSED, - State.SPIN_DOWN).onEntry(() -> new Thread(tests.get(0)).start()) - .onExit(() -> tests.get(0).setRunning(false)); - stateMachineConfig.configure(State.SPIN_DOWN).permit(Trigger.TIME_HAS_PASSED, State.SPIN_UP) - .permit(Trigger.FINISHED, State.FINISHED).onEntry(() -> { - for (IMotorController motor : tests.get(index).getItemsToTest()) { - motor.set(ControlMode.PercentOutput, 0); - } - index++; - }); - this.stateMachine = new StateMachine<>(State.SPIN_UP, stateMachineConfig); + stateMachineConfig.configure(State.EXECUTING) + .permit(Trigger.TIME_HAS_PASSED, State.SPIN_DOWN) + .onEntry(() -> new Thread(tests.get(index).getTest()).start()) + .onExit(() -> tests.get(index).getTest().setRunning(false)); + stateMachineConfig.configure(State.SPIN_DOWN) + .permit(Trigger.TIME_HAS_PASSED, State.SPIN_UP) + .permit(Trigger.FINISHED, State.FINISHED) + .onEntry(() -> { + for (IMotorController motor : tests.get(index).getTest().getItemsToTest()) { + motor.set(ControlMode.PercentOutput, 0); + } + index++; + }); + this.stateMachine = new StateMachine<>(State.INITIALIZING, stateMachineConfig); } public ControllersMultiTester addControllerGroup(IMotorController... controllerGroup) { - tests.add(new BurnoutTester(controllerGroup)); + addControllerGroup(this::setMotorToFull, controllerGroup); + return this; + } + + @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) + public ControllersMultiTester addControllerGroup(Consumer function, + IMotorController... controllerGroup) { + tests.add(new TesterAndCommand(new BurnoutTester(controllerGroup), function)); return this; } public ControllersMultiTester addEncoderGroup(ChickenTalon... controllerGroup) { - tests.add(new EncoderTester(controllerGroup)); + addEncoderGroup(this::setMotorToFull, controllerGroup); + return this; + } + + @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) + public ControllersMultiTester addEncoderGroup(Consumer function, + ChickenTalon... controllerGroup) { + tests.add(new TesterAndCommand(new EncoderTester(controllerGroup), function)); return this; } @Override protected void initialize() { + timer.reset(); timer.start(); } @@ -72,10 +95,11 @@ protected void execute() { @Override protected void end() { // Very optimized yes no duplicate code either - for (AbstractTester tester : tests) { - for (IMotorController controller : tester.getItemsToTest()) { + for (TesterAndCommand testerAndCommand : tests) { + for (IMotorController controller : testerAndCommand.getTest().getItemsToTest()) { int failureCount = 0; - for (ResultWithMetadata result : tester.getStoredResults(controller)) { + for (ResultWithMetadata result : testerAndCommand.getTest() + .getStoredResults(controller)) { if (result.getResult().equals(Boolean.TRUE)) { failureCount++; } @@ -83,7 +107,7 @@ protected void end() { if (failureCount > 0) { DriverStation .reportError("Motor " + controller.getDeviceID() + " reported " + failureCount + - " failures of type " + tester, false); + " failures of type " + testerAndCommand.getTest(), false); } } } @@ -95,8 +119,13 @@ protected boolean isFinished() { return stateMachine.getState().equals(State.FINISHED); } + private void setMotorToFull(IMotorController motor) { + motor.setNeutralMode(NeutralMode.Coast); + motor.set(ControlMode.PercentOutput, 1.0); + } + private enum State { - SPIN_UP(0.25), EXECUTING(3), SPIN_DOWN(0.25), FINISHED; + INITIALIZING(0), SPIN_UP(0.25), EXECUTING(1), SPIN_DOWN(0), FINISHED; private final Double timeToComplete; @@ -117,4 +146,25 @@ private enum Trigger { TIME_HAS_PASSED, FINISHED } + private class TesterAndCommand { + + private AbstractTester test; + private Consumer function; + + private TesterAndCommand( + AbstractTester test, + Consumer function) { + this.test = test; + this.function = function; + } + + private AbstractTester getTest() { + return test; + } + + private Consumer getFunction() { + return function; + } + } + } From e16bcc8c049a215c7ab3746c9b9f41681ec8e352 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 6 Dec 2018 17:50:01 -0800 Subject: [PATCH 14/16] Add Javadoc --- .../testers/motor/ControllersMultiTester.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java index 3616a043..af06a940 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -15,6 +15,10 @@ import org.team1540.rooster.testers.ResultWithMetadata; import org.team1540.rooster.wrappers.ChickenTalon; +/** + * Simple automated testing for motors. Add the motors with the specified test using the builder + * methods, then run the command. + */ public class ControllersMultiTester extends Command { private Timer timer = new Timer(); @@ -22,6 +26,11 @@ public class ControllersMultiTester extends Command { private List tests = new LinkedList<>(); private int index = 0; + /** + * Default constructor. Note that this class follows a builder pattern; use + * {@link #addControllerGroup(IMotorController...)} and + * {@link #addEncoderGroup(ChickenTalon...)} to add the motors. + */ public ControllersMultiTester() { StateMachineConfig stateMachineConfig = new StateMachineConfig<>(); stateMachineConfig.configure(State.INITIALIZING) @@ -49,11 +58,25 @@ public ControllersMultiTester() { this.stateMachine = new StateMachine<>(State.INITIALIZING, stateMachineConfig); } + /** + * Add a group of motors to test together with the default function (disables braking and sets + * the motors to full.) Currently uses only {@link BurnoutTester}. + * + * @param controllerGroup The motors to add. + * @return this + */ public ControllersMultiTester addControllerGroup(IMotorController... controllerGroup) { addControllerGroup(this::setMotorToFull, controllerGroup); return this; } + /** + * Add a group of motors to test together with the specified function. Currently uses only + * {@link BurnoutTester}. + * @param function The function to apply before running the tests. + * @param controllerGroup The motors to add. + * @return this + */ @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) public ControllersMultiTester addControllerGroup(Consumer function, IMotorController... controllerGroup) { @@ -61,11 +84,24 @@ public ControllersMultiTester addControllerGroup(Consumer func return this; } + /** + * Add a group of motors with encoders to test together with the default function (disables + * braking and sets the motors to full.) Currently uses only {@link EncoderTester}. + * @param controllerGroup The motors to add. + * @return this + */ public ControllersMultiTester addEncoderGroup(ChickenTalon... controllerGroup) { addEncoderGroup(this::setMotorToFull, controllerGroup); return this; } + /** + * Add a group of motors with encoders to test together with the specified function. Currently + * uses only {@link EncoderTester}. + * @param function The function to apply before running the tests. + * @param controllerGroup The motors to add. + * @return this + */ @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) public ControllersMultiTester addEncoderGroup(Consumer function, ChickenTalon... controllerGroup) { From c5e3f66dafaa05b57531e0d4923b483f013f6afe Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Fri, 7 Dec 2018 15:35:49 -0800 Subject: [PATCH 15/16] Disable state machine logging, update version --- build.gradle | 1 + lib/build.gradle | 3 ++- .../team1540/rooster/testers/motor/ControllersMultiTester.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b4c9a5f1..90414b38 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ subprojects { repositories { mavenCentral() + maven {url "https://jitpack.io/"} } dependencies { diff --git a/lib/build.gradle b/lib/build.gradle index d7d2ef6a..fbee41ea 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -46,5 +46,6 @@ dependencies { compile "openrio.powerup:MatchData:2018.01.07" compile "com.google.guava:guava:27.0-jre" compile "org.apache.commons:commons-math3:3.6.1" - compile "com.github.oxo42:stateless4j:2.5.0" + // Last release was in 2014, so we're just pinning it to this commit instead + compile "com.github.oxo42:stateless4j:3dd512049f" } diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java index af06a940..14f12d0e 100644 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -56,6 +56,7 @@ public ControllersMultiTester() { index++; }); this.stateMachine = new StateMachine<>(State.INITIALIZING, stateMachineConfig); + this.stateMachine.setShouldLog(false); } /** From a9be247d0ebab9a977e8f1c1a873c5e934c25803 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Fri, 7 Dec 2018 15:41:25 -0800 Subject: [PATCH 16/16] Add EncoderTEster and ControllersMultiTester to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 039138b0..72c78984 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,9 @@ A system to easily set tuning fields through WPILib `Preferences`. Various classes for testing common things. - `BurnoutTester` for testing if motors are burned out. +- `EncoderTester` for testing if motors have working encoders attached. - `SimpleControllersTester` for easily running motors. +- `ControllersMultiTester` for automatically running multiple tests across motor groups. #### Triggers