From 2889084e5da12292446ad5bb9750f39a0da32d2f Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Thu, 13 Dec 2018 10:42:10 -0800 Subject: [PATCH 01/29] Make addAllSendables() follow a builder pattern --- .../rooster/testers/motor/SimpleControllersTester.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 95cc63d0..055f0de4 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 @@ -148,11 +148,13 @@ public void initSendable(SendableBuilder builder) { /** * Add all the sendables to the {@link SmartDashboard}. Includes chooser for selecting the - * active {@link IMotorController} and information about the tester. + * active {@link IMotorController} and information about the tester. Follows a builder pattern. + * @return Follows a builder pattern. */ - public void addAllSendables() { + public SimpleControllersTester addAllSendables() { SmartDashboard.putData("Controller tester info", this); SmartDashboard.putData("Controller choose", getControllerChooser()); + return this; } From b91f5479f3daff5b8e855b3ad73aa17165983571 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Sat, 1 Dec 2018 13:01:22 -0800 Subject: [PATCH 02/29] Update Gradle to 5.0 --- build.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 54329 -> 55741 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- gradlew | 2 +- gradlew.bat | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 90414b38..784560cd 100644 --- a/build.gradle +++ b/build.gradle @@ -10,9 +10,9 @@ buildscript { } } -task wrapper(type: Wrapper) { - gradleVersion = '4.4' - distributionUrl = "https://services.gradle.org/distributions/gradle-4.4-all.zip" +wrapper { + gradleVersion = '5.0' + distributionType = Wrapper.DistributionType.ALL } subprojects { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 01b8bf6b1f99cad9213fc495b33ad5bbab8efd20..457aad0d98108420a977756b7145c93c8910b076 100644 GIT binary patch delta 49862 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%0Apdl4 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�zFYskr8AG`JlN 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 I{pY*?2Sj}k*8l(j diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1510c29a..ee671127 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Nov 30 17:46:10 PST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/gradlew b/gradlew index cccdd3d5..af6708ff 100755 --- a/gradlew +++ b/gradlew @@ -28,7 +28,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index f9553162..6d57edc7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome From d168f821d5f7ac64ab1837f59c92955fc947d8e9 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Thu, 3 Jan 2019 22:06:58 -0800 Subject: [PATCH 03/29] Fix braces lol --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 784560cd..5c3b1e47 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ subprojects { repositories { mavenCentral() - maven {url "https://jitpack.io/"} + maven { url "https://jitpack.io/" } } dependencies { From bb61e9500048d49f4333ee25aa59de0cb6bf47ad Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Thu, 3 Jan 2019 23:16:57 -0800 Subject: [PATCH 04/29] Update to new GradleRIO This is broken at the moment so [skip-ci], but I'm just uploading so someone on discord can take a look --- build.gradle | 9 ++++++++- lib/build.gradle | 39 +++++++++++++++++++++++++++++---------- test/build.gradle | 45 ++++++++++++++++++++++----------------------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index 5c3b1e47..36ac93d3 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,14 @@ subprojects { repositories { mavenCentral() - maven { url "https://jitpack.io/" } + maven { + name "JitPack" + url "https://jitpack.io/" + } + maven { + name "Jaci" + url "http://dev.imjac.in/maven/" + } } dependencies { diff --git a/lib/build.gradle b/lib/build.gradle index fbee41ea..1b651476 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -16,9 +16,15 @@ artifacts { } repositories { - maven { url "http://first.wpi.edu/FRC/roborio/maven/release" } - maven { url "http://dev.imjac.in/maven/" } - maven { url "https://raw.githubusercontent.com/Open-RIO/Maven-Mirror/master/m2" } + maven { + name "WPI" + url "http://first.wpi.edu/FRC/roborio/maven/release" + } + maven { + name "CTRE" + url "http://devsite.ctr-electronics.com/maven/release/" + } + // NavX software is hosted on Maven Central cuz they're good at things } javadoc { @@ -35,15 +41,28 @@ javadoc { dependencies { - compile "edu.wpi.first.wpilibj:wpilibj-java:2018.4.1" - compile "edu.wpi.first.ntcore:ntcore-java:4.1.0" - compile "edu.wpi.first.wpiutil:wpiutil-java:3.2.0" + // We only bring in the java interfaces here. JNIs (native code) get brought in by GradleRIO, + // but we don't need those to compile our java code. + + // FRC dependencies + + // WPI et al + compile "edu.wpi.first.wpilibj:wpilibj-java:2019.1.1" + compile "edu.wpi.first.ntcore:ntcore-java:2019.1.1" compile "org.opencv:opencv-java:3.2.0" - compile "edu.wpi.first.cscore:cscore-java:1.3.0" - compile "openrio.mirror.third.ctre:CTRE-phoenix-java:5.3.1.0" - compile "openrio.mirror.third.kauailabs:navx-java:3.0.346" + compile "edu.wpi.first.cscore:cscore-java:2019.1.1" + + // CTRE + compile "com.ctre.phoenix:api-java:5.12.0" + compile "com.ctre.phoenix:wpiapi-java:5.12.0" + + // NavX + compile "com.kauailabs.navx.frc:navx-java:3.1.340" + + // Pathfinder compile "jaci.pathfinder:Pathfinder-Java:1.8" - compile "openrio.powerup:MatchData:2018.01.07" + + // Non-FRC depedencies compile "com.google.guava:guava:27.0-jre" compile "org.apache.commons:commons-math3:3.6.1" // Last release was in 2014, so we're just pinning it to this commit instead diff --git a/test/build.gradle b/test/build.gradle index 1745e1e3..7a79a48b 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -1,33 +1,15 @@ -import jaci.openrio.gradle.GradleRIOPlugin -import jaci.openrio.gradle.frc.FRCJavaArtifact -import jaci.openrio.gradle.frc.RoboRIO - plugins { - id "jaci.openrio.gradle.GradleRIO" version "2018.03.06" -} - -wpi { - ctreVersion = "5.3.1.0" + id "edu.wpi.first.GradleRIO" version "2019.1.1" } -dependencies { - compile project(":lib") - compile wpilib() - compile ctre() - compile navx() - compile pathfinder() -} - -def TEAM = 1540 - deploy { targets { - target("roborio", RoboRIO) { - team = TEAM + roboRIO("roborio") { + team = frc.getTeamOrDefault(1540) } } artifacts { - artifact('frcJava', FRCJavaArtifact) { + frcJavaArtifact('frcJava') { targets << "roborio" /* IMPORTANT: With the "deploy-debug" property set (i.e. -Pdeploy-debug is on the @@ -40,6 +22,23 @@ deploy { } } + +dependencies { + compile(project(":lib")) { + exclude group: "edu.wpi.first.wpilibj" + exclude group: "edu.wpi.first.ntcore" + exclude group: "edu.wpi.first.cscore" + exclude group: "org.opencv" + exclude group: "com.ctre.phoenix" + exclude group: "com.kauailabs.navx.frc" + } + + compile wpi.deps.wpilib() + compile wpi.deps.vendor.java() + nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) + nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) +} + jar { // declare the robot class property as an input, as normally since we // set it at runtime this wouldn't run if the class changed due to up-to-date checking @@ -57,7 +56,7 @@ jar { } else { println "Creating JAR for robot ${project.robotClass}" } - manifest GradleRIOPlugin.javaManifest(project.robotClass) + manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(project.robotClass) } } from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } From 2e75ee52a326705813b829288d89e8ebb33e4cd4 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 00:45:10 -0800 Subject: [PATCH 05/29] Update to 2019 Whew. So, the 2019 GradleRIO and WPILib update borked a lot of the library structure (fortunately almost none of the code) and so I've restructured the thing. Again. Now, instead of having two gradle projects, we have two source sets (i.e. folders in src/). There's still some improvements that need to be made. * The choose-your-own-robot-class function is entirely broken and I'm not sure how I can fix it without some ugly stuff. * Not totally sure about new API stuff, and various bits and bobs may need to be migrated for them to work properly. * The testing framework is temporarily deleted because CTRE updates borked it. @edelmanjm --- .idea/compiler.xml | 6 +- .idea/misc.xml | 8 +- build.gradle | 120 ++++++++-- gradle/wrapper/gradle-wrapper.properties | 4 +- lib/build.gradle | 70 ------ lib/settings.gradle | 0 .../rooster/testers/AbstractTester.java | 209 ------------------ .../rooster/testers/ResultWithMetadata.java | 32 --- .../org/team1540/rooster/testers/Tester.java | 91 -------- .../rooster/testers/motor/BurnoutTester.java | 174 --------------- .../testers/motor/ControllersMultiTester.java | 207 ----------------- .../rooster/testers/motor/EncoderTester.java | 145 ------------ .../motor/SimpleControllersTester.java | 159 ------------- .../rooster/testers/package-info.java | 4 - settings.gradle | 3 - .../team1540/rooster/ChickenSubsystem.java | 2 +- .../java/org/team1540/rooster/Utilities.java | 0 .../adjustables/AdjustableManager.java | 0 .../adjustables/SmartDashboardGet.java | 0 .../adjustables/SmartDashboardPut.java | 0 .../rooster/adjustables/Telemetry.java | 0 .../rooster/adjustables/TelemetryType.java | 0 .../team1540/rooster/adjustables/Tunable.java | 0 .../rooster/adjustables/TunableType.java | 0 .../rooster/drive/JoystickScaling.java | 0 .../rooster/drive/NoJoystickScaling.java | 0 .../org/team1540/rooster/drive/PidDrive.java | 0 .../rooster/drive/PidDriveConfiguration.java | 0 .../rooster/drive/PidDriveFactory.java | 0 .../rooster/drive/PowerJoystickScaling.java | 0 .../team1540/rooster/drive/package-info.java | 0 .../pipeline/AdvancedArcadeJoystickInput.java | 0 .../rooster/drive/pipeline/CTREOutput.java | 0 .../rooster/drive/pipeline/DriveData.java | 0 .../drive/pipeline/FeedForwardProcessor.java | 0 .../rooster/drive/pipeline/Input.java | 0 .../rooster/drive/pipeline/Output.java | 0 .../rooster/drive/pipeline/Processor.java | 0 .../drive/pipeline/SimpleJoystickInput.java | 0 .../rooster/drive/pipeline/TankDriveData.java | 0 .../rooster/drive/pipeline/UnitScaler.java | 0 .../rooster/drive/pipeline/package-info.java | 0 .../MotionProfilingProperties.java | 0 .../motionprofiling/RunMotionProfiles.java | 0 .../rooster/power/PowerManageable.java | 0 .../team1540/rooster/power/PowerManager.java | 0 .../rooster/power/PowerTelemetry.java | 0 .../team1540/rooster/power/package-info.java | 0 .../rooster/preferencemanager/Preference.java | 0 .../preferencemanager/PreferenceManager.java | 0 .../preferencemanager/TuningClass.java | 0 .../preferencemanager/package-info.java | 0 .../team1540/rooster/triggers/AxisButton.java | 0 .../team1540/rooster/triggers/DPadAxis.java | 0 .../team1540/rooster/triggers/DPadButton.java | 0 .../rooster/triggers/SimpleButton.java | 0 .../rooster/triggers/StrictDPadButton.java | 0 .../rooster/triggers/package-info.java | 0 .../team1540/rooster/util/AsyncCommand.java | 0 .../team1540/rooster/util/DashboardUtils.java | 0 .../org/team1540/rooster/util/Executable.java | 0 .../rooster/util/SimpleAsyncCommand.java | 0 .../team1540/rooster/util/SimpleCommand.java | 0 .../util/SimpleConditionalCommand.java | 0 .../rooster/util/SimpleLoopCommand.java | 0 .../team1540/rooster/util/package-info.java | 0 .../rooster/util/robots/PIDTuningRobot.java | 0 .../rooster/util/robots/package-info.java | 0 .../rooster/wrappers/ChickenController.java | 4 +- .../rooster/wrappers/ChickenTalon.java | 4 +- .../rooster/wrappers/ChickenVictor.java | 159 +------------ .../team1540/rooster/wrappers/RevBlinken.java | 0 .../rooster/wrappers/package-info.java | 0 .../drive/pipeline/PipelineExtensions.kt | 0 .../rooster/testing/AdjustableTestRobot.java | 0 .../testing/AsyncCommandTestRobot.java | 0 .../rooster/testing/BlinkenTestRobot.java | 2 +- .../rooster/testing/DriveTestRobot.java | 0 .../rooster/testing/HelloWorldTestRobot.java | 0 .../rooster/testing/PreferencesTestRobot.java | 0 .../rooster/testing/package-info.java | 0 .../rooster/testing/DrivePipelineTesting.kt | 0 test/build.gradle | 63 ------ test/settings.gradle | 0 vendordeps/NavX.json | 33 +++ vendordeps/PathfinderOLD.json | 87 ++++++++ vendordeps/Phoenix.json | 87 ++++++++ 87 files changed, 326 insertions(+), 1347 deletions(-) delete mode 100644 lib/build.gradle delete mode 100644 lib/settings.gradle delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/Tester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java delete mode 100644 lib/src/main/java/org/team1540/rooster/testers/package-info.java rename {lib/src => src}/main/java/org/team1540/rooster/ChickenSubsystem.java (99%) rename {lib/src => src}/main/java/org/team1540/rooster/Utilities.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/AdjustableManager.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/SmartDashboardGet.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/SmartDashboardPut.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/Telemetry.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/TelemetryType.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/Tunable.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/adjustables/TunableType.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/JoystickScaling.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/NoJoystickScaling.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/PidDrive.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/PidDriveConfiguration.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/PidDriveFactory.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/PowerJoystickScaling.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/AdvancedArcadeJoystickInput.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/CTREOutput.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/DriveData.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/FeedForwardProcessor.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/Input.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/Output.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/Processor.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/SimpleJoystickInput.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/TankDriveData.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/UnitScaler.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/drive/pipeline/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/motionprofiling/MotionProfilingProperties.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/motionprofiling/RunMotionProfiles.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/power/PowerManageable.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/power/PowerManager.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/power/PowerTelemetry.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/power/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/preferencemanager/Preference.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/preferencemanager/PreferenceManager.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/preferencemanager/TuningClass.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/preferencemanager/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/AxisButton.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/DPadAxis.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/DPadButton.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/SimpleButton.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/StrictDPadButton.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/triggers/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/AsyncCommand.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/DashboardUtils.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/Executable.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/SimpleAsyncCommand.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/SimpleCommand.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/SimpleConditionalCommand.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/SimpleLoopCommand.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/robots/PIDTuningRobot.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/util/robots/package-info.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/wrappers/ChickenController.java (99%) rename {lib/src => src}/main/java/org/team1540/rooster/wrappers/ChickenTalon.java (99%) rename {lib/src => src}/main/java/org/team1540/rooster/wrappers/ChickenVictor.java (78%) rename {lib/src => src}/main/java/org/team1540/rooster/wrappers/RevBlinken.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/wrappers/package-info.java (100%) rename {lib/src => src}/main/kotlin/org/team1540/rooster/drive/pipeline/PipelineExtensions.kt (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/AdjustableTestRobot.java (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/BlinkenTestRobot.java (98%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/DriveTestRobot.java (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/HelloWorldTestRobot.java (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/PreferencesTestRobot.java (100%) rename {test/src/main => src/testbots}/java/org/team1540/rooster/testing/package-info.java (100%) rename {test/src/main => src/testbots}/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt (100%) delete mode 100644 test/build.gradle delete mode 100644 test/settings.gradle create mode 100644 vendordeps/NavX.json create mode 100644 vendordeps/PathfinderOLD.json create mode 100644 vendordeps/Phoenix.json diff --git a/.idea/compiler.xml b/.idea/compiler.xml index f84dc7d8..806c7b0e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -4,12 +4,14 @@ + - - + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index 2b874b6a..6e0a941b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 36ac93d3..0acbe3e3 100644 --- a/build.gradle +++ b/build.gradle @@ -10,35 +10,113 @@ buildscript { } } -wrapper { - gradleVersion = '5.0' - distributionType = Wrapper.DistributionType.ALL +plugins { + id "java" + id "maven" + id "edu.wpi.first.GradleRIO" version "2019.1.1" } -subprojects { - apply plugin: "java" - apply plugin: "kotlin" +apply plugin: "kotlin" - compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +repositories { + mavenCentral() + maven { + name "JitPack" + url "https://jitpack.io/" } +} - repositories { - mavenCentral() - maven { - name "JitPack" - url "https://jitpack.io/" +javadoc { + options { + header '' + links 'http://first.wpi.edu/FRC/roborio/release/docs/java/', 'http://www.ctr-electronics.com/downloads/api/java/html/' + linksOffline('https://docs.oracle.com/javase/8/docs/api/', 'https://docs.oracle.com/javase/8/docs/api/') + } + options.addStringOption("doctitle", "ROOSTER API") + options.addStringOption("windowtitle", "ROOSTER API") + options.addBooleanOption("-allow-script-in-comments", true) + options.addBooleanOption('Xdoclint:all,-missing', true) +} + + +dependencies { + // FRC dependencies + compile wpi.deps.wpilib() + compile wpi.deps.vendor.java() + nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) + nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) + + // Non-FRC depedencies + compile 'org.jetbrains:annotations:16.0.3' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + + compile "com.google.guava:guava:27.0-jre" + compile "org.apache.commons:commons-math3:3.6.1" + // Last release was in 2014, so we're just pinning it to this commit instead + compile "com.github.oxo42:stateless4j:3dd512049f" +} + +sourceSets { + testbots { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + } +} + +configurations { + testbotsCompile.extendsFrom compile + testbotsRuntimeOnly.extendsFrom runtimeOnly +} + +deploy { + targets { + roboRIO("roborio") { + team = frc.getTeamOrDefault(1540) } - maven { - name "Jaci" - url "http://dev.imjac.in/maven/" + } + artifacts { + frcJavaArtifact('frcJava') { + targets << "roborio" + /* + IMPORTANT: With the "deploy-debug" property set (i.e. -Pdeploy-debug is on the + command line), the robot code will not start until you connect your debugger to the + robot. Connect by running the "Remote Robot Debug" run configuration which should be + shared through version control. Alternatively, remove this line and redeploy. + */ + debug = project.hasProperty("deploy-debug") } } +} - dependencies { - compile 'org.jetbrains:annotations:16.0.3' - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +jar.dependsOn(compileTestbotsJava) + +jar { + inputs.files(compileTestbotsJava.outputs.files) + gradle.taskGraph.whenReady { + if (gradle.taskGraph.hasTask(":deploy")) { // fully qualified task name is needed + from ((configurations.compile.collect { + it.isDirectory() ? it : zipTree(it) + }) + sourceSets.testbots.output) + } } } + +wrapper { + gradleVersion = '5.0' + distributionType = Wrapper.DistributionType.ALL +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ee671127..4a248e4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists +distributionPath=permwrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-all.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +zipStorePath=permwrapper/dists diff --git a/lib/build.gradle b/lib/build.gradle deleted file mode 100644 index 1b651476..00000000 --- a/lib/build.gradle +++ /dev/null @@ -1,70 +0,0 @@ -apply plugin: "maven" - -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - -repositories { - maven { - name "WPI" - url "http://first.wpi.edu/FRC/roborio/maven/release" - } - maven { - name "CTRE" - url "http://devsite.ctr-electronics.com/maven/release/" - } - // NavX software is hosted on Maven Central cuz they're good at things -} - -javadoc { - options { - header '' - links 'http://first.wpi.edu/FRC/roborio/release/docs/java/', 'http://www.ctr-electronics.com/downloads/api/java/html/' - linksOffline('https://docs.oracle.com/javase/8/docs/api/', 'https://docs.oracle.com/javase/8/docs/api/') - } - options.addStringOption("doctitle", "ROOSTER API") - options.addStringOption("windowtitle", "ROOSTER API") - options.addBooleanOption("-allow-script-in-comments", true) - options.addBooleanOption('Xdoclint:all,-missing', true) -} - - -dependencies { - // We only bring in the java interfaces here. JNIs (native code) get brought in by GradleRIO, - // but we don't need those to compile our java code. - - // FRC dependencies - - // WPI et al - compile "edu.wpi.first.wpilibj:wpilibj-java:2019.1.1" - compile "edu.wpi.first.ntcore:ntcore-java:2019.1.1" - compile "org.opencv:opencv-java:3.2.0" - compile "edu.wpi.first.cscore:cscore-java:2019.1.1" - - // CTRE - compile "com.ctre.phoenix:api-java:5.12.0" - compile "com.ctre.phoenix:wpiapi-java:5.12.0" - - // NavX - compile "com.kauailabs.navx.frc:navx-java:3.1.340" - - // Pathfinder - compile "jaci.pathfinder:Pathfinder-Java:1.8" - - // Non-FRC depedencies - compile "com.google.guava:guava:27.0-jre" - compile "org.apache.commons:commons-math3:3.6.1" - // 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/settings.gradle b/lib/settings.gradle deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java deleted file mode 100644 index 56eabb27..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.team1540.rooster.testers; - -import com.google.common.collect.EvictingQueue; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * A basic implementation of the Tester interface using essentially EvictingQueues for result - * storage. - */ -@SuppressWarnings({"unused", "UnstableApiUsage"}) -public abstract class AbstractTester implements Tester { - - private static float DEFAULT_LOG_TIME = 150; - private static int DEFAULT_UPDATE_DELAY = 2500; - - private int updateDelay; - private boolean running = true; - @NotNull - private List itemsToTest; - @NotNull - private Function test; - @Nullable - private List> runConditions; - @NotNull - private Map> storedResults; - - /** - * Construct a new instance with the default queue depth. - * - * @param test The test to execute. - * @param itemsToTest The items to apply the test to. - * @param runConditions The conditions that must be met before the test will be executed on an - * item. - */ - protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @Nullable List> runConditions) { - this(test, itemsToTest, runConditions, (int) DEFAULT_LOG_TIME / (DEFAULT_UPDATE_DELAY / 1000)); - } - - /** - * Construct a new instance, specifying the logTime and updateDelay to calculate the queue depth. - * - * @param test The test to execute. - * @param itemsToTest The items to apply the test to. - * @param runConditions The conditions that must be met before the test will be executed on an - * item. - * @param logTime The maximum length of time for which we want to store the results. Note that - * this is just used for estimating the queue depth based on the update delay, not actually - * checked against while running. - * @param updateDelay The delay between the test being run on the items. - */ - protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @Nullable List> runConditions, float logTime, int updateDelay) { - this(test, itemsToTest, runConditions, - (int) (logTime / ((float) updateDelay / 1000f))); - this.updateDelay = updateDelay; - } - - /** - * - * Construct a new instance with a given queueDepth. - * @param test The test to execute. - * @param itemsToTest The items to apply the test to. - * @param runConditions The conditions that must be met before the test will be executed on an - * item. - * @param queueDepth The maximum number of items that the {@link EvictingQueue} can hold. - */ - @SuppressWarnings("WeakerAccess") - protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, - @Nullable List> runConditions, int queueDepth) { - this.test = test; - this.itemsToTest = itemsToTest; - this.runConditions = runConditions; - this.storedResults = new HashMap<>(itemsToTest.size()); - // TODO I feel like there's a cleaner way of doing this - for (T t : itemsToTest) { - this.storedResults.put(t, new ResultStorage<>(queueDepth)); - } - } - - @Override - @NotNull - public Function getTest() { - return test; - } - - @Override - public void setTest(@NotNull Function test) { - this.test = test; - } - - @Override - @Nullable - public List> getRunConditions() { - return runConditions; - } - - @Override - public void setRunConditions( - @Nullable List> runConditions) { - this.runConditions = runConditions; - } - - - @Override - @NotNull - public List getItemsToTest() { - return Collections.unmodifiableList(itemsToTest); - } - - @Override - @NotNull - public EvictingQueue> getStoredResults(T key) { - return storedResults.get(key).queuedResults; - } - - @Override - @Nullable - public ResultWithMetadata peekMostRecentResult(T key) { - return storedResults.get(key).lastResult; - } - - @Override - public int getUpdateDelay() { - return updateDelay; - } - - @Override - public int setUpdateDelay(int delay) { - int oldUpdateDelay = this.updateDelay; - this.updateDelay = delay; - return oldUpdateDelay; - } - - @Override - public boolean setRunning(boolean status) { - boolean oldRunning = this.running; - this.running = status; - return 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!) - */ - protected void periodic() { - for (T t : itemsToTest) { - // 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()); - } - } - - @Override - public void run() { - while (true) { - - if (running) { - periodic(); - } - - try { - Thread.sleep(updateDelay); - } catch (InterruptedException e) { - // End the thread - return; - } - } - } - - /** - * A class for handling the storage of results. Basically just so the tail can actually be - * peeked at. - * @param The returned type to store. - */ - private class ResultStorage { - - @Nullable - private ResultWithMetadata lastResult; - @NotNull - private EvictingQueue> queuedResults; - - private ResultStorage(int queueDepth) { - this.queuedResults = EvictingQueue.create(queueDepth); - } - - private void addResult(A result, long timeStampMillis) { - ResultWithMetadata resultWithMetadata = new ResultWithMetadata<>(result, timeStampMillis); - this.lastResult = resultWithMetadata; - queuedResults.add(resultWithMetadata); - } - - } - -} diff --git a/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java b/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java deleted file mode 100644 index ef0be5ed..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.team1540.rooster.testers; - -import org.jetbrains.annotations.NotNull; - -/** - * Class for encapsulating results. Includes the result and a timestamp. - * - * @param The result type. - */ -public class ResultWithMetadata { - - @NotNull - private final R result; - private final long timestampMillis; - - ResultWithMetadata(@NotNull R result, long timestampMillis) { - this.result = result; - this.timestampMillis = timestampMillis; - } - - @SuppressWarnings("WeakerAccess") - @NotNull - public R getResult() { - return result; - } - - @SuppressWarnings("unused") - public long getTimestampMillis() { - return timestampMillis; - } - -} diff --git a/lib/src/main/java/org/team1540/rooster/testers/Tester.java b/lib/src/main/java/org/team1540/rooster/testers/Tester.java deleted file mode 100644 index 51e98dae..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/Tester.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.team1540.rooster.testers; - -import com.google.common.collect.EvictingQueue; -import java.util.List; -import java.util.function.Function; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * An interface for testing things. - * - * @param The type of item being tested. - * @param The return type of the test results. - */ -@SuppressWarnings("unused") -public interface Tester extends Runnable { - - @NotNull - Function getTest(); - - void setTest(@NotNull Function tests); - - /** - * Gets the conditions that must be met before the test will be executed on an item. - * - * @return An potentially {@link List} of the run conditions, or null if there are none (always - * execute.) - */ - @SuppressWarnings("UnstableApiUsage") - @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. - * - * @return An {@link EvictingQueue} of the items that are being tested. - */ - @SuppressWarnings("UnstableApiUsage") - @NotNull // TODO how to annotate as unmodifiable? - List getItemsToTest(); - - /** - * Get the results of the test being run. - * - * @param key The item to get the results for. - * @return An {@link EvictingQueue} of {@link ResultWithMetadata} that encapsulate the returned - * values. - */ - @SuppressWarnings("UnstableApiUsage") - @NotNull - EvictingQueue> getStoredResults(T key); - - /** - * Gets the most recent result of the the test without impacting the stored results. - * - * @param key The item to get the results for. - * @return A {@link ResultWithMetadata} that encapsulate the returned value. - */ - @Nullable - ResultWithMetadata peekMostRecentResult(T key); - - /** - * Gets the delay between the test being run on the items. - * @return The delay in milliseconds. - */ - int getUpdateDelay(); - - /** - * Set the delay between the test being run on the items. - * @param delay The new delay in milliseconds. - * @return The previous delay in milliseconds. - */ - @SuppressWarnings("UnusedReturnValue") - int setUpdateDelay(int delay); - - /** - * Set if the tests should be running. Note that this does not stop the thread, only the actual - * execution of the tests. - * @param status The new status. - * @return The old status. - */ - boolean setRunning(boolean status); -} 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 deleted file mode 100644 index 9ae39f9d..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java +++ /dev/null @@ -1,174 +0,0 @@ -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; -import java.util.List; -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 - * motors and reporting low outliers or checking if a single motor is below the cutoff. - */ -@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; - 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, 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. - */ - @SuppressWarnings("WeakerAccess") - public BurnoutTester(IMotorController... motorsToTest) { - this(Arrays.asList(motorsToTest)); - } - - /** - * 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. - */ - @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, null, 150, 500); - this.setTest(motorsToTest.size() > 2 ? this::testBurnoutMultiMotor : - this::testBurnoutSingleMotor); - } - - /** - * 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 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 - protected void periodic() { - double[] currents = getItemsToTest().stream().mapToDouble(IMotorController::getOutputCurrent) - .toArray(); - medianCurrent = medianCalculator.evaluate(currents); - stdDevCurrent = stdDevCalculator.evaluate(currents); - super.periodic(); - } - - @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 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. - */ - @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 - 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); - } - builder.addDoubleProperty("Median current", () -> medianCurrent, null); - } -} 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 deleted file mode 100644 index 14f12d0e..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.team1540.rooster.testers.motor; - -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; -import edu.wpi.first.wpilibj.Timer; -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; - -/** - * 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(); - private StateMachine stateMachine; - 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) - .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).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(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); - this.stateMachine.setShouldLog(false); - } - - /** - * 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) { - tests.add(new TesterAndCommand(new BurnoutTester(controllerGroup), function)); - 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) { - tests.add(new TesterAndCommand(new EncoderTester(controllerGroup), function)); - return this; - } - - @Override - protected void initialize() { - timer.reset(); - 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 (TesterAndCommand testerAndCommand : tests) { - for (IMotorController controller : testerAndCommand.getTest().getItemsToTest()) { - int failureCount = 0; - for (ResultWithMetadata result : testerAndCommand.getTest() - .getStoredResults(controller)) { - if (result.getResult().equals(Boolean.TRUE)) { - failureCount++; - } - } - if (failureCount > 0) { - DriverStation - .reportError("Motor " + controller.getDeviceID() + " reported " + failureCount + - " failures of type " + testerAndCommand.getTest(), false); - } - } - } - System.out.println("Finished testing"); - } - - @Override - 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 { - INITIALIZING(0), SPIN_UP(0.25), EXECUTING(1), SPIN_DOWN(0), 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 - } - - 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; - } - } - -} 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 deleted file mode 100644 index f3dcc8e0..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ /dev/null @@ -1,145 +0,0 @@ -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; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -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 - * 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 IMotorController IMotorControllers} to compare to each other. - */ - @SuppressWarnings("WeakerAccess") - 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 IMotorController IMotorControllers} 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 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 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 - && Math.abs(controller.getSelectedSensorVelocity(0)) < 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 encoder 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 encoder 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 IMotorController}. - * - * @param builder The {@link SendableBuilder} to use. - */ - @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)); - if (result.isPresent()) { - return result.get().getResult(); - } else { - return false; - } - }, null); - } - } -} 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 deleted file mode 100644 index 95cc63d0..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.team1540.rooster.testers.motor; - -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; -import edu.wpi.first.wpilibj.command.Command; -import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; -import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; -import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -import java.util.NavigableMap; -import java.util.Optional; -import java.util.TreeMap; -import org.team1540.rooster.util.SimpleCommand; - -/** - * A simple command for testing a series of {@link IMotorController IMotorControllers}. Simply - * add the controllers, specify control bindings or use the defaults, and you're ready! Select - * the motor in the SmartDashboard or move next/previous with buttons and control the percent - * output of the motor with a joystick axis. - */ -public class SimpleControllersTester extends Command implements Sendable { - - private static final int DEFAULT_JOYSTICK_ID = 0; - private static final int DEFAULT_AXIS_ID = 5; - private static final int DEFAULT_NEXT_BUTTON_ID = 1; - private static final int DEFAULT_PREVIOUS_BUTTON_ID = 2; - - private Joystick joystick; - private int axisId; - - // Is the retrieval time poor, despite the need for a lot of retrievals? Yes. Are we ignoring that - // because the size of the map is small? Also yes. - private NavigableMap controllers = new TreeMap<>(); - - // Do not manually update this value. Instead, call setCurrentController() - private IMotorController currentController; - - private SendableChooser controllerChooser = new SendableChooser<>(); - - /** - * Construct a new instance with the default bindings on {@link Joystick} 0. On an Xbox - * controller, the right thumbstick y-axis controls {@link ControlMode}.PERCENT_OUTPUT, the A - * button goes to the next {@link IMotorController}, and the B button goes to the previous - * {@link IMotorController}. - * - * @param controllers The {@link IMotorController IMotorControllers} to test. - */ - public SimpleControllersTester(IMotorController... controllers) { - this(new Joystick(DEFAULT_JOYSTICK_ID), DEFAULT_AXIS_ID, DEFAULT_NEXT_BUTTON_ID, - DEFAULT_PREVIOUS_BUTTON_ID, controllers); - } - - /** - * Construct a new instance with the specified bindings. - * @param joystick The joystick port to use. - * @param axisId The axis on the joystick to use. - * @param nextButtonId The button to use to proceed to the next {@link IMotorController}. - * @param previousButtonId The button to use to proceed to the nex {@link IMotorController}. - * @param controllers The {@link IMotorController IMotorControllers} to test. - */ - @SuppressWarnings("WeakerAccess") - public SimpleControllersTester(Joystick joystick, int axisId, int nextButtonId, - int previousButtonId, - IMotorController... controllers) { - this.joystick = joystick; - this.axisId = axisId; - - // Make the buttons go the next and previous controller, or loop around if not available - new JoystickButton(this.joystick, nextButtonId).whenPressed(new SimpleCommand("Next " - + "controller ID", - () -> setCurrentController( - Optional.ofNullable(this.controllers.higherKey(this.currentController.getDeviceID())) - .orElse(this.controllers.firstKey())))); - new JoystickButton(this.joystick, previousButtonId).whenPressed(new SimpleCommand("Previous " - + "controller ID", - () -> setCurrentController( - Optional.ofNullable(this.controllers.lowerKey(this.currentController.getDeviceID())) - .orElse(this.controllers.lastKey())))); - - // Add a chooser that you can use to select the controller and initialize it to null, - // corresponding with buttons should be used - this.controllerChooser.addObject("Use buttons", null); - for (IMotorController controller : controllers) { - 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()); - } - - /** - * Checks every tick if the chooser is set to a controller or to use buttons, then sets the - * output according to the value of the active joystick. - */ - @Override - protected void execute() { - if (controllerChooser.getSelected() != null) { - setCurrentController(controllerChooser.getSelected()); - } - currentController.set(ControlMode.PercentOutput, joystick.getRawAxis(axisId)); - } - - /** - * Updates the currently active {@link IMotorController}. - * @param newId The ID of the new {@link IMotorController}. - */ - @SuppressWarnings("WeakerAccess") - public void setCurrentController(int newId) { - if (currentController != null) { - currentController.set(ControlMode.PercentOutput, 0); - } - currentController = controllers.get(newId); - } - - /** - * Prevents the command from finishing. - * @return false. - */ - @Override - protected boolean isFinished() { - return false; - } - - /** - * Gets the chooser used for selecting the current {@link IMotorController}. - * @return A chooser. - */ - @SuppressWarnings("WeakerAccess") - public SendableChooser getControllerChooser() { - return controllerChooser; - } - - /** - * Adds a single property showing the current active controller. - * @param builder The thing to add the things to. - */ - @Override - public void initSendable(SendableBuilder builder) { - builder.addDoubleProperty("Controller ID", () -> this.currentController.getDeviceID(), - (id) -> setCurrentController((int) id)); - } - - /** - * Add all the sendables to the {@link SmartDashboard}. Includes chooser for selecting the - * active {@link IMotorController} and information about the tester. - */ - public void addAllSendables() { - SmartDashboard.putData("Controller tester info", this); - SmartDashboard.putData("Controller choose", getControllerChooser()); - } - - -} diff --git a/lib/src/main/java/org/team1540/rooster/testers/package-info.java b/lib/src/main/java/org/team1540/rooster/testers/package-info.java deleted file mode 100644 index 33dbb24a..00000000 --- a/lib/src/main/java/org/team1540/rooster/testers/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Various classes for testing common things. - */ -package org.team1540.rooster.testers; diff --git a/settings.gradle b/settings.gradle index b76d609c..7a8dfca1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1 @@ rootProject.name = 'ROOSTER' - -include 'lib' -include 'test' diff --git a/lib/src/main/java/org/team1540/rooster/ChickenSubsystem.java b/src/main/java/org/team1540/rooster/ChickenSubsystem.java similarity index 99% rename from lib/src/main/java/org/team1540/rooster/ChickenSubsystem.java rename to src/main/java/org/team1540/rooster/ChickenSubsystem.java index 2b106f66..f7140b23 100644 --- a/lib/src/main/java/org/team1540/rooster/ChickenSubsystem.java +++ b/src/main/java/org/team1540/rooster/ChickenSubsystem.java @@ -37,7 +37,7 @@ public double getCurrent() { double sum = 0; for (ChickenController motor : motors.keySet()) { if (motor instanceof ChickenTalon) { - sum += motor.getOutputCurrent(); + sum += ((ChickenTalon) motor).getOutputCurrent(); } } return sum; diff --git a/lib/src/main/java/org/team1540/rooster/Utilities.java b/src/main/java/org/team1540/rooster/Utilities.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/Utilities.java rename to src/main/java/org/team1540/rooster/Utilities.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/AdjustableManager.java b/src/main/java/org/team1540/rooster/adjustables/AdjustableManager.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/AdjustableManager.java rename to src/main/java/org/team1540/rooster/adjustables/AdjustableManager.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/SmartDashboardGet.java b/src/main/java/org/team1540/rooster/adjustables/SmartDashboardGet.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/SmartDashboardGet.java rename to src/main/java/org/team1540/rooster/adjustables/SmartDashboardGet.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/SmartDashboardPut.java b/src/main/java/org/team1540/rooster/adjustables/SmartDashboardPut.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/SmartDashboardPut.java rename to src/main/java/org/team1540/rooster/adjustables/SmartDashboardPut.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/Telemetry.java b/src/main/java/org/team1540/rooster/adjustables/Telemetry.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/Telemetry.java rename to src/main/java/org/team1540/rooster/adjustables/Telemetry.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/TelemetryType.java b/src/main/java/org/team1540/rooster/adjustables/TelemetryType.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/TelemetryType.java rename to src/main/java/org/team1540/rooster/adjustables/TelemetryType.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/Tunable.java b/src/main/java/org/team1540/rooster/adjustables/Tunable.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/Tunable.java rename to src/main/java/org/team1540/rooster/adjustables/Tunable.java diff --git a/lib/src/main/java/org/team1540/rooster/adjustables/TunableType.java b/src/main/java/org/team1540/rooster/adjustables/TunableType.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/adjustables/TunableType.java rename to src/main/java/org/team1540/rooster/adjustables/TunableType.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/JoystickScaling.java b/src/main/java/org/team1540/rooster/drive/JoystickScaling.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/JoystickScaling.java rename to src/main/java/org/team1540/rooster/drive/JoystickScaling.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/NoJoystickScaling.java b/src/main/java/org/team1540/rooster/drive/NoJoystickScaling.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/NoJoystickScaling.java rename to src/main/java/org/team1540/rooster/drive/NoJoystickScaling.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/PidDrive.java b/src/main/java/org/team1540/rooster/drive/PidDrive.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/PidDrive.java rename to src/main/java/org/team1540/rooster/drive/PidDrive.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/PidDriveConfiguration.java b/src/main/java/org/team1540/rooster/drive/PidDriveConfiguration.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/PidDriveConfiguration.java rename to src/main/java/org/team1540/rooster/drive/PidDriveConfiguration.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/PidDriveFactory.java b/src/main/java/org/team1540/rooster/drive/PidDriveFactory.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/PidDriveFactory.java rename to src/main/java/org/team1540/rooster/drive/PidDriveFactory.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/PowerJoystickScaling.java b/src/main/java/org/team1540/rooster/drive/PowerJoystickScaling.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/PowerJoystickScaling.java rename to src/main/java/org/team1540/rooster/drive/PowerJoystickScaling.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/package-info.java b/src/main/java/org/team1540/rooster/drive/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/package-info.java rename to src/main/java/org/team1540/rooster/drive/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/AdvancedArcadeJoystickInput.java b/src/main/java/org/team1540/rooster/drive/pipeline/AdvancedArcadeJoystickInput.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/AdvancedArcadeJoystickInput.java rename to src/main/java/org/team1540/rooster/drive/pipeline/AdvancedArcadeJoystickInput.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/CTREOutput.java b/src/main/java/org/team1540/rooster/drive/pipeline/CTREOutput.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/CTREOutput.java rename to src/main/java/org/team1540/rooster/drive/pipeline/CTREOutput.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/DriveData.java b/src/main/java/org/team1540/rooster/drive/pipeline/DriveData.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/DriveData.java rename to src/main/java/org/team1540/rooster/drive/pipeline/DriveData.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/FeedForwardProcessor.java b/src/main/java/org/team1540/rooster/drive/pipeline/FeedForwardProcessor.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/FeedForwardProcessor.java rename to src/main/java/org/team1540/rooster/drive/pipeline/FeedForwardProcessor.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/Input.java b/src/main/java/org/team1540/rooster/drive/pipeline/Input.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/Input.java rename to src/main/java/org/team1540/rooster/drive/pipeline/Input.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/Output.java b/src/main/java/org/team1540/rooster/drive/pipeline/Output.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/Output.java rename to src/main/java/org/team1540/rooster/drive/pipeline/Output.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/Processor.java b/src/main/java/org/team1540/rooster/drive/pipeline/Processor.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/Processor.java rename to src/main/java/org/team1540/rooster/drive/pipeline/Processor.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/SimpleJoystickInput.java b/src/main/java/org/team1540/rooster/drive/pipeline/SimpleJoystickInput.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/SimpleJoystickInput.java rename to src/main/java/org/team1540/rooster/drive/pipeline/SimpleJoystickInput.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/TankDriveData.java b/src/main/java/org/team1540/rooster/drive/pipeline/TankDriveData.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/TankDriveData.java rename to src/main/java/org/team1540/rooster/drive/pipeline/TankDriveData.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/UnitScaler.java b/src/main/java/org/team1540/rooster/drive/pipeline/UnitScaler.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/UnitScaler.java rename to src/main/java/org/team1540/rooster/drive/pipeline/UnitScaler.java diff --git a/lib/src/main/java/org/team1540/rooster/drive/pipeline/package-info.java b/src/main/java/org/team1540/rooster/drive/pipeline/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/drive/pipeline/package-info.java rename to src/main/java/org/team1540/rooster/drive/pipeline/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/motionprofiling/MotionProfilingProperties.java b/src/main/java/org/team1540/rooster/motionprofiling/MotionProfilingProperties.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/motionprofiling/MotionProfilingProperties.java rename to src/main/java/org/team1540/rooster/motionprofiling/MotionProfilingProperties.java diff --git a/lib/src/main/java/org/team1540/rooster/motionprofiling/RunMotionProfiles.java b/src/main/java/org/team1540/rooster/motionprofiling/RunMotionProfiles.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/motionprofiling/RunMotionProfiles.java rename to src/main/java/org/team1540/rooster/motionprofiling/RunMotionProfiles.java diff --git a/lib/src/main/java/org/team1540/rooster/power/PowerManageable.java b/src/main/java/org/team1540/rooster/power/PowerManageable.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/power/PowerManageable.java rename to src/main/java/org/team1540/rooster/power/PowerManageable.java diff --git a/lib/src/main/java/org/team1540/rooster/power/PowerManager.java b/src/main/java/org/team1540/rooster/power/PowerManager.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/power/PowerManager.java rename to src/main/java/org/team1540/rooster/power/PowerManager.java diff --git a/lib/src/main/java/org/team1540/rooster/power/PowerTelemetry.java b/src/main/java/org/team1540/rooster/power/PowerTelemetry.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/power/PowerTelemetry.java rename to src/main/java/org/team1540/rooster/power/PowerTelemetry.java diff --git a/lib/src/main/java/org/team1540/rooster/power/package-info.java b/src/main/java/org/team1540/rooster/power/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/power/package-info.java rename to src/main/java/org/team1540/rooster/power/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/preferencemanager/Preference.java b/src/main/java/org/team1540/rooster/preferencemanager/Preference.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/preferencemanager/Preference.java rename to src/main/java/org/team1540/rooster/preferencemanager/Preference.java diff --git a/lib/src/main/java/org/team1540/rooster/preferencemanager/PreferenceManager.java b/src/main/java/org/team1540/rooster/preferencemanager/PreferenceManager.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/preferencemanager/PreferenceManager.java rename to src/main/java/org/team1540/rooster/preferencemanager/PreferenceManager.java diff --git a/lib/src/main/java/org/team1540/rooster/preferencemanager/TuningClass.java b/src/main/java/org/team1540/rooster/preferencemanager/TuningClass.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/preferencemanager/TuningClass.java rename to src/main/java/org/team1540/rooster/preferencemanager/TuningClass.java diff --git a/lib/src/main/java/org/team1540/rooster/preferencemanager/package-info.java b/src/main/java/org/team1540/rooster/preferencemanager/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/preferencemanager/package-info.java rename to src/main/java/org/team1540/rooster/preferencemanager/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/AxisButton.java b/src/main/java/org/team1540/rooster/triggers/AxisButton.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/AxisButton.java rename to src/main/java/org/team1540/rooster/triggers/AxisButton.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/DPadAxis.java b/src/main/java/org/team1540/rooster/triggers/DPadAxis.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/DPadAxis.java rename to src/main/java/org/team1540/rooster/triggers/DPadAxis.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/DPadButton.java b/src/main/java/org/team1540/rooster/triggers/DPadButton.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/DPadButton.java rename to src/main/java/org/team1540/rooster/triggers/DPadButton.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/SimpleButton.java b/src/main/java/org/team1540/rooster/triggers/SimpleButton.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/SimpleButton.java rename to src/main/java/org/team1540/rooster/triggers/SimpleButton.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/StrictDPadButton.java b/src/main/java/org/team1540/rooster/triggers/StrictDPadButton.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/StrictDPadButton.java rename to src/main/java/org/team1540/rooster/triggers/StrictDPadButton.java diff --git a/lib/src/main/java/org/team1540/rooster/triggers/package-info.java b/src/main/java/org/team1540/rooster/triggers/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/triggers/package-info.java rename to src/main/java/org/team1540/rooster/triggers/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/util/AsyncCommand.java b/src/main/java/org/team1540/rooster/util/AsyncCommand.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/AsyncCommand.java rename to src/main/java/org/team1540/rooster/util/AsyncCommand.java diff --git a/lib/src/main/java/org/team1540/rooster/util/DashboardUtils.java b/src/main/java/org/team1540/rooster/util/DashboardUtils.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/DashboardUtils.java rename to src/main/java/org/team1540/rooster/util/DashboardUtils.java diff --git a/lib/src/main/java/org/team1540/rooster/util/Executable.java b/src/main/java/org/team1540/rooster/util/Executable.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/Executable.java rename to src/main/java/org/team1540/rooster/util/Executable.java diff --git a/lib/src/main/java/org/team1540/rooster/util/SimpleAsyncCommand.java b/src/main/java/org/team1540/rooster/util/SimpleAsyncCommand.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/SimpleAsyncCommand.java rename to src/main/java/org/team1540/rooster/util/SimpleAsyncCommand.java diff --git a/lib/src/main/java/org/team1540/rooster/util/SimpleCommand.java b/src/main/java/org/team1540/rooster/util/SimpleCommand.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/SimpleCommand.java rename to src/main/java/org/team1540/rooster/util/SimpleCommand.java diff --git a/lib/src/main/java/org/team1540/rooster/util/SimpleConditionalCommand.java b/src/main/java/org/team1540/rooster/util/SimpleConditionalCommand.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/SimpleConditionalCommand.java rename to src/main/java/org/team1540/rooster/util/SimpleConditionalCommand.java diff --git a/lib/src/main/java/org/team1540/rooster/util/SimpleLoopCommand.java b/src/main/java/org/team1540/rooster/util/SimpleLoopCommand.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/SimpleLoopCommand.java rename to src/main/java/org/team1540/rooster/util/SimpleLoopCommand.java diff --git a/lib/src/main/java/org/team1540/rooster/util/package-info.java b/src/main/java/org/team1540/rooster/util/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/package-info.java rename to src/main/java/org/team1540/rooster/util/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/util/robots/PIDTuningRobot.java b/src/main/java/org/team1540/rooster/util/robots/PIDTuningRobot.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/robots/PIDTuningRobot.java rename to src/main/java/org/team1540/rooster/util/robots/PIDTuningRobot.java diff --git a/lib/src/main/java/org/team1540/rooster/util/robots/package-info.java b/src/main/java/org/team1540/rooster/util/robots/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/util/robots/package-info.java rename to src/main/java/org/team1540/rooster/util/robots/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenController.java b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java similarity index 99% rename from lib/src/main/java/org/team1540/rooster/wrappers/ChickenController.java rename to src/main/java/org/team1540/rooster/wrappers/ChickenController.java index a3bbcf73..06aa9b91 100644 --- a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenController.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java @@ -508,14 +508,14 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, * * @return Position of selected sensor in raw sensor units per decisecond. */ - double getSelectedSensorPosition(); + int getSelectedSensorPosition(); /** * Get the selected sensor velocity. * * @return Velocity of selected sensor in raw sensor units per decisecond. */ - double getSelectedSensorVelocity(); + int getSelectedSensorVelocity(); int getStatusFramePeriod(StatusFrameEnhanced frame); diff --git a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java b/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java similarity index 99% rename from lib/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java rename to src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java index c96fbb44..b46a851a 100644 --- a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java @@ -721,7 +721,7 @@ public int getQuadratureVelocity() { * @return Position of selected sensor in raw sensor units per decisecond. */ @Override - public double getSelectedSensorPosition() { + public int getSelectedSensorPosition() { return super.getSelectedSensorPosition(defaultPidIdx); } @@ -731,7 +731,7 @@ public double getSelectedSensorPosition() { * @return Velocity of selected sensor in raw sensor units per decisecond. */ @Override - public double getSelectedSensorVelocity() { + public int getSelectedSensorVelocity() { return super.getSelectedSensorVelocity(defaultPidIdx); } diff --git a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java similarity index 78% rename from lib/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java rename to src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java index d0ae38a4..335d8b8c 100644 --- a/lib/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java @@ -16,7 +16,7 @@ import com.ctre.phoenix.motorcontrol.can.MotControllerJNI; import com.ctre.phoenix.motorcontrol.can.VictorSPX; -public class ChickenVictor extends VictorSPX implements ChickenController { +public class ChickenVictor extends VictorSPX { ControlMode controlMode = ControlMode.PercentOutput; int defaultPidIdx = 0; @@ -28,440 +28,287 @@ public ChickenVictor(int deviceNumber) { super(deviceNumber); } - @Override public ErrorCode clearMotionProfileHasUnderrun() { return super.clearMotionProfileHasUnderrun(defaultTimeoutMs); } - @Override public ErrorCode clearStickyFaults() { return super.clearStickyFaults(defaultTimeoutMs); } - @Override public ErrorCode configAllowableClosedloopError(int slotIdx, int allowableClosedLoopError) { return super .configAllowableClosedloopError(slotIdx, allowableClosedLoopError, defaultTimeoutMs); } - @Override public ErrorCode configClosedloopRamp(double secondsFromNeutralToFull) { return super.configClosedloopRamp(secondsFromNeutralToFull, defaultTimeoutMs); } - @Override public ErrorCode configOpenloopRamp(double secondsFromNeutralToFull) { return super.configOpenloopRamp(secondsFromNeutralToFull, defaultTimeoutMs); } - @Override public ErrorCode configForwardLimitSwitchSource(LimitSwitchSource type, LimitSwitchNormal normalOpenOrClose) { return super.configForwardLimitSwitchSource(type, normalOpenOrClose, defaultTimeoutMs); } - @Override public ErrorCode configForwardLimitSwitchSource(RemoteLimitSwitchSource type, LimitSwitchNormal normalOpenOrClose, int deviceID) { return super .configForwardLimitSwitchSource(type, normalOpenOrClose, deviceID, defaultTimeoutMs); } - @Override public ErrorCode configForwardSoftLimitEnable(boolean enable) { return super.configForwardSoftLimitEnable(enable, defaultTimeoutMs); } - @Override public ErrorCode configForwardSoftLimitThreshold(int forwardSensorLimit) { return super.configForwardSoftLimitThreshold(forwardSensorLimit, defaultTimeoutMs); } - @Override public int configGetCustomParam(int paramIndex) { return super.configGetCustomParam(paramIndex, defaultTimeoutMs); } - @Override public double configGetParameter(ParamEnum param, int ordinal) { return super.configGetParameter(param, ordinal, defaultTimeoutMs); } - @Override public double configGetParameter(int param, int ordinal) { return super.configGetParameter(param, ordinal, defaultTimeoutMs); } - @Override public ErrorCode configMaxIntegralAccumulator(int slotIdx, double iaccum) { return super.configMaxIntegralAccumulator(slotIdx, iaccum, defaultTimeoutMs); } - @Override public ErrorCode configMotionAcceleration(int sensorUnitsPer100msPerSec) { return super.configMotionAcceleration(sensorUnitsPer100msPerSec, defaultTimeoutMs); } - @Override public ErrorCode configMotionCruiseVelocity(int sensorUnitsPer100ms) { return super.configMotionCruiseVelocity(sensorUnitsPer100ms, defaultTimeoutMs); } - @Override public ErrorCode configNeutralDeadband(double percentDeadband) { return super.configNeutralDeadband(percentDeadband, defaultTimeoutMs); } - @Override public ErrorCode configNominalOutputForward(double percentOut) { return super.configNominalOutputForward(percentOut, defaultTimeoutMs); } - @Override public ErrorCode configNominalOutputReverse(double percentOut) { return super.configNominalOutputReverse(percentOut, defaultTimeoutMs); } - @Override public ErrorCode configPeakOutputForward(double percentOut) { peakOutputForward = percentOut; return super.configPeakOutputForward(percentOut, defaultTimeoutMs); } - @Override public ErrorCode configPeakOutputReverse(double percentOut) { peakOutputReverse = percentOut; return super.configPeakOutputReverse(percentOut, defaultTimeoutMs); } - @Override public double getPeakOutputForward() { return peakOutputForward; } - @Override public double getPeakOutputReverse() { return peakOutputReverse; } - @Override public ErrorCode configRemoteFeedbackFilter(int deviceID, RemoteSensorSource remoteSensorSource, int remoteOrdinal) { return super .configRemoteFeedbackFilter(deviceID, remoteSensorSource, remoteOrdinal, defaultTimeoutMs); } - @Override public ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, LimitSwitchNormal normalOpenOrClose, int deviceID) { return super .configReverseLimitSwitchSource(type, normalOpenOrClose, deviceID, defaultTimeoutMs); } - @Override public ErrorCode configReverseSoftLimitEnable(boolean enable) { return super.configReverseSoftLimitEnable(enable, defaultTimeoutMs); } - @Override public ErrorCode configReverseSoftLimitThreshold(int reverseSensorLimit) { return super.configReverseSoftLimitThreshold(reverseSensorLimit, defaultTimeoutMs); } - @Override public ErrorCode configSelectedFeedbackSensor(RemoteFeedbackDevice feedbackDevice, int pidIdx) { return super.configSelectedFeedbackSensor(feedbackDevice, pidIdx, defaultTimeoutMs); } - @Override public ErrorCode configSelectedFeedbackSensor(RemoteFeedbackDevice feedbackDevice) { return super.configSelectedFeedbackSensor(feedbackDevice, defaultPidIdx, defaultTimeoutMs); } - @Override public ErrorCode configSelectedFeedbackSensor(FeedbackDevice feedbackDevice, int pidIdx) { return super.configSelectedFeedbackSensor(feedbackDevice, pidIdx, defaultTimeoutMs); } - @Override public ErrorCode configSelectedFeedbackSensor(FeedbackDevice feedbackDevice) { return super.configSelectedFeedbackSensor(feedbackDevice, defaultPidIdx, defaultTimeoutMs); } - @Override public ErrorCode configSensorTerm(SensorTerm sensorTerm, FeedbackDevice feedbackDevice) { return super.configSensorTerm(sensorTerm, feedbackDevice, defaultTimeoutMs); } - @Override public ErrorCode configSetCustomParam(int newValue, int paramIndex) { return super.configSetCustomParam(newValue, paramIndex, defaultTimeoutMs); } - @Override public ErrorCode configSetParameter(ParamEnum param, double value, int subValue, int ordinal) { return super.configSetParameter(param, value, subValue, ordinal, defaultTimeoutMs); } - @Override public ErrorCode configSetParameter(int param, double value, int subValue, int ordinal) { return super.configSetParameter(param, value, subValue, ordinal, defaultTimeoutMs); } - @Override public ErrorCode configVelocityMeasurementPeriod(int period) { int retval = MotControllerJNI .ConfigVelocityMeasurementPeriod(m_handle, period, defaultTimeoutMs); return ErrorCode.valueOf(retval); } - @Override public ErrorCode configVelocityMeasurementWindow(int windowSize) { return super.configVelocityMeasurementWindow(windowSize, defaultTimeoutMs); } - @Override public ErrorCode configVoltageCompSaturation(double voltage) { return super.configVoltageCompSaturation(voltage, defaultTimeoutMs); } - @Override public ErrorCode configVoltageMeasurementFilter(int filterWindowSamples) { return super.configVoltageMeasurementFilter(filterWindowSamples, defaultTimeoutMs); } - @Override public ErrorCode config_IntegralZone(int slotIdx, int izone) { return super.config_IntegralZone(slotIdx, izone, defaultTimeoutMs); } - @Override public ErrorCode config_kD(int slotIdx, double value) { return super.config_kD(slotIdx, value, defaultTimeoutMs); } - @Override public ErrorCode config_kF(int slotIdx, double value) { return super.config_kF(slotIdx, value, defaultTimeoutMs); } - @Override public ErrorCode config_kI(int slotIdx, double value) { return super.config_kI(slotIdx, value, defaultTimeoutMs); } - @Override public ErrorCode config_kP(int slotIdx, double value) { return super.config_kP(slotIdx, value, defaultTimeoutMs); } - @Override - public int getAnalogIn() { - return getSensorCollection().getAnalogIn(); - } - - @Override - public int getAnalogInRaw() { - return getSensorCollection().getAnalogInRaw(); - } - - @Override - public int getAnalogInVel() { - return getSensorCollection().getAnalogInVel(); - } - - @Override public int getClosedLoopError() { return super.getClosedLoopError(defaultPidIdx); } - @Override public ControlMode getControlMode() { return controlMode; } - @Override public void setControlMode(ControlMode controlMode) { this.controlMode = controlMode; } - @Override public int getDefaultPidIdx() { return defaultPidIdx; } - @Override public void setDefaultPidIdx(int defaultPidIdx) { this.defaultPidIdx = defaultPidIdx; } - @Override public int getDefaultTimeoutMs() { return defaultTimeoutMs; } - @Override public void setDefaultTimeoutMs(int defaultTimeoutMs) { this.defaultTimeoutMs = defaultTimeoutMs; } - @Override @Deprecated public double getEncoderCodesPerRev() { return 0; } - @Override @Deprecated public void setEncoderCodesPerRev(double encoderCodesPerRev) { } - @Override public double getErrorDerivative() { return super.getErrorDerivative(defaultPidIdx); } - @Override public double getIntegralAccumulator() { return super.getIntegralAccumulator(defaultPidIdx); } - @Override - public boolean getPinStateQuadA() { - return getSensorCollection().getPinStateQuadA(); - } - - @Override - public boolean getPinStateQuadB() { - return getSensorCollection().getPinStateQuadB(); - } - - @Override - public boolean getPinStateQuadIdx() { - return getSensorCollection().getPinStateQuadIdx(); - } - - @Override - public int getPulseWidthPosition() { - return getSensorCollection().getPulseWidthPosition(); - } - - @Override - public int getPulseWidthRiseToFallUs() { - return getSensorCollection().getPulseWidthRiseToFallUs(); - } - - @Override - public int getPulseWidthRiseToRiseUs() { - return getSensorCollection().getPulseWidthRiseToRiseUs(); - } - - @Override - public int getPulseWidthVelocity() { - return getSensorCollection().getPulseWidthVelocity(); - } - - @Override - public int getQuadraturePosition() { - return getSensorCollection().getQuadraturePosition(); - } - - @Override - public int getQuadratureVelocity() { - return getSensorCollection().getQuadratureVelocity(); - } - - @Override - public double getSelectedSensorPosition() { + public int getSelectedSensorPosition() { return super.getSelectedSensorPosition(defaultPidIdx); } - @Override - public double getSelectedSensorVelocity() { + public int getSelectedSensorVelocity() { return super.getSelectedSensorVelocity(defaultPidIdx); } - @Override public int getStatusFramePeriod(StatusFrameEnhanced frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } - @Override public int getStatusFramePeriod(int frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } - @Override public int getStatusFramePeriod(StatusFrame frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } - @Override - public boolean isFwdLimitSwitchClosed() { - return getSensorCollection().isFwdLimitSwitchClosed(); - } - - @Override - public boolean isRevLimitSwitchClosed() { - return getSensorCollection().isRevLimitSwitchClosed(); - } - - @Override public void selectProfileSlot(int slotIdx) { super.selectProfileSlot(slotIdx, defaultPidIdx); } - @Override public void set(double outputValue) { super.set(controlMode, outputValue); } - @Override - public ErrorCode setAnalogPosition(int newPosition) { - return getSensorCollection().setAnalogPosition(newPosition, defaultTimeoutMs); - } - - @Override public void setBrake(boolean brake) { super.setNeutralMode(brake ? NeutralMode.Brake : NeutralMode.Coast); } - @Override public ErrorCode setIntegralAccumulator(double iaccum, int pidIdx) { return super.setIntegralAccumulator(iaccum, pidIdx, defaultTimeoutMs); } - @Override public ErrorCode setIntegralAccumulator(double iaccum) { return super.setIntegralAccumulator(iaccum, defaultPidIdx, defaultTimeoutMs); } - @Override - public ErrorCode setPulseWidthPosition(int newPosition) { - return getSensorCollection().setPulseWidthPosition(newPosition, defaultTimeoutMs); - } - - @Override - public ErrorCode setQuadraturePosition(int newPosition) { - return getSensorCollection().setQuadraturePosition(newPosition, defaultTimeoutMs); - } - - @Override public ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx) { return super.setSelectedSensorPosition(sensorPos, pidIdx, defaultTimeoutMs); } - @Override public ErrorCode setSelectedSensorPosition(int sensorPos) { return super.setSelectedSensorPosition(sensorPos, defaultPidIdx, defaultTimeoutMs); } - @Override public ErrorCode setStatusFramePeriod(int frameValue, int periodMs) { return super.setStatusFramePeriod(frameValue, periodMs, defaultTimeoutMs); } - @Override public ErrorCode setStatusFramePeriod(StatusFrame frame, int periodMs) { return super.setStatusFramePeriod(frame, periodMs, defaultTimeoutMs); } diff --git a/lib/src/main/java/org/team1540/rooster/wrappers/RevBlinken.java b/src/main/java/org/team1540/rooster/wrappers/RevBlinken.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/wrappers/RevBlinken.java rename to src/main/java/org/team1540/rooster/wrappers/RevBlinken.java diff --git a/lib/src/main/java/org/team1540/rooster/wrappers/package-info.java b/src/main/java/org/team1540/rooster/wrappers/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/wrappers/package-info.java rename to src/main/java/org/team1540/rooster/wrappers/package-info.java diff --git a/lib/src/main/kotlin/org/team1540/rooster/drive/pipeline/PipelineExtensions.kt b/src/main/kotlin/org/team1540/rooster/drive/pipeline/PipelineExtensions.kt similarity index 100% rename from lib/src/main/kotlin/org/team1540/rooster/drive/pipeline/PipelineExtensions.kt rename to src/main/kotlin/org/team1540/rooster/drive/pipeline/PipelineExtensions.kt diff --git a/test/src/main/java/org/team1540/rooster/testing/AdjustableTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/AdjustableTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java diff --git a/test/src/main/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java diff --git a/test/src/main/java/org/team1540/rooster/testing/BlinkenTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java similarity index 98% rename from test/src/main/java/org/team1540/rooster/testing/BlinkenTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java index 826e1c9e..81e06b9e 100644 --- a/test/src/main/java/org/team1540/rooster/testing/BlinkenTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java @@ -34,7 +34,7 @@ public void robotPeriodic() { int newID = (int) SmartDashboard.getNumber("Spark ID", currentID); if (newID != currentID || blinken == null) { if (blinken != null) { - blinken.free(); + blinken.close(); } blinken = new RevBlinken(newID); diff --git a/test/src/main/java/org/team1540/rooster/testing/DriveTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/DriveTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java diff --git a/test/src/main/java/org/team1540/rooster/testing/HelloWorldTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/HelloWorldTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java diff --git a/test/src/main/java/org/team1540/rooster/testing/PreferencesTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/PreferencesTestRobot.java rename to src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java diff --git a/test/src/main/java/org/team1540/rooster/testing/package-info.java b/src/testbots/java/org/team1540/rooster/testing/package-info.java similarity index 100% rename from test/src/main/java/org/team1540/rooster/testing/package-info.java rename to src/testbots/java/org/team1540/rooster/testing/package-info.java diff --git a/test/src/main/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt b/src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt similarity index 100% rename from test/src/main/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt rename to src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt diff --git a/test/build.gradle b/test/build.gradle deleted file mode 100644 index 7a79a48b..00000000 --- a/test/build.gradle +++ /dev/null @@ -1,63 +0,0 @@ -plugins { - id "edu.wpi.first.GradleRIO" version "2019.1.1" -} - -deploy { - targets { - roboRIO("roborio") { - team = frc.getTeamOrDefault(1540) - } - } - artifacts { - frcJavaArtifact('frcJava') { - targets << "roborio" - /* - IMPORTANT: With the "deploy-debug" property set (i.e. -Pdeploy-debug is on the - command line), the robot code will not start until you connect your debugger to the - robot. Connect by running the "Remote Robot Debug" run configuration which should be - shared through version control. Alternatively, remove this line and redeploy. - */ - debug = project.hasProperty("deploy-debug") - } - } -} - - -dependencies { - compile(project(":lib")) { - exclude group: "edu.wpi.first.wpilibj" - exclude group: "edu.wpi.first.ntcore" - exclude group: "edu.wpi.first.cscore" - exclude group: "org.opencv" - exclude group: "com.ctre.phoenix" - exclude group: "com.kauailabs.navx.frc" - } - - compile wpi.deps.wpilib() - compile wpi.deps.vendor.java() - nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) - nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) -} - -jar { - // declare the robot class property as an input, as normally since we - // set it at runtime this wouldn't run if the class changed due to up-to-date checking - inputs.property("robotClass", project.hasProperty("robotClass") ? project.robotClass : "") - gradle.taskGraph.whenReady { - /* - check if we are deploying to the robot. If we're building in CI, for example, the - robot class doesn't need to be set. - this is inside the gradle.taskGraph.whenReady so that we don't check if the deploy - task is being run before Gradle has figured it out. - */ - if (gradle.taskGraph.hasTask(":test:deploy")) { // fully qualified task name is needed - if (!project.hasProperty("robotClass")) { - throw new GradleException("Robot class not set. Pass a value in on the command line by adding -ProbotClass=.") - } else { - println "Creating JAR for robot ${project.robotClass}" - } - manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(project.robotClass) - } - } - from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } -} diff --git a/test/settings.gradle b/test/settings.gradle deleted file mode 100644 index e69de29b..00000000 diff --git a/vendordeps/NavX.json b/vendordeps/NavX.json new file mode 100644 index 00000000..173fb45f --- /dev/null +++ b/vendordeps/NavX.json @@ -0,0 +1,33 @@ +{ + "fileName": "NavX.json", + "name": "KauaiLabs_navX_FRC", + "version": "3.1.339", + "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", + "mavenUrls": [ + "https://repo1.maven.org/maven2/" + ], + "jsonUrl": "https://www.kauailabs.com/dist/frc/2019/navx_frc.json", + "javaDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-java", + "version": "3.1.339" + } + ], + "jniDependencies": [], + "cppDependencies": [ + { + "groupId": "com.kauailabs.navx.frc", + "artifactId": "navx-cpp", + "version": "3.1.339", + "headerClassifier": "headers", + "sourcesClassifier": "sources", + "sharedLibrary": false, + "libName": "navx_frc", + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena" + ] + } + ] +} diff --git a/vendordeps/PathfinderOLD.json b/vendordeps/PathfinderOLD.json new file mode 100644 index 00000000..f7157aef --- /dev/null +++ b/vendordeps/PathfinderOLD.json @@ -0,0 +1,87 @@ +{ + "fileName": "PathfinderOLD.json", + "name": "PathfinderOld", + "version": "2019.1.10", + "uuid": "7194a2d4-2860-4bcc-86c0-97879737d875", + "uuid": "7194a2d4-2860-4bcc-86c0-97879737d875", + "mavenUrls": [ + "https://dev.imjac.in/maven" + ], + "jsonUrl": "https://dev.imjac.in/maven/jaci/pathfinder/PathfinderOLD-latest.json", + "cppDependencies": [ + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-Core", + "version": "2019.1.10", + "libName": "pathfinder", + "configuration": "native_pathfinder_old", + "headerClassifier": "headers", + "sharedLibrary": true, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxx86-64", + "windowsx86-64", + "osxx86-64", + "linuxathena", + "linuxraspbian" + ] + }, + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-FRCSupport", + "version": "2019.1.10", + "libName": "pathfinder_frc", + "configuration": "native_pathfinder_old", + "headerClassifier": "headers", + "binaryPlatforms": [ + ] + } + ], + "javaDependencies": [ + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-Java", + "version": "2019.1.10" + }, + { + "groupId": "jaci.jniloader", + "artifactId": "JNILoader", + "version": "1.0.1" + }, + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-FRCSupport", + "version": "2019.1.10" + } + ], + "jniDependencies": [ + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-JNI", + "version": "2019.1.10", + "isJar": true, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxx86-64", + "windowsx86-64", + "osxx86-64", + "linuxathena", + "linuxraspbian" + ] + }, + { + "groupId": "jaci.pathfinder", + "artifactId": "Pathfinder-CoreJNI", + "version": "2019.1.10", + "isJar": true, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxx86-64", + "windowsx86-64", + "osxx86-64", + "linuxathena", + "linuxraspbian" + ] + } + ] +} diff --git a/vendordeps/Phoenix.json b/vendordeps/Phoenix.json new file mode 100644 index 00000000..df8d7600 --- /dev/null +++ b/vendordeps/Phoenix.json @@ -0,0 +1,87 @@ +{ + "fileName": "Phoenix.json", + "name": "CTRE-Phoenix", + "version": "5.12.0", + "uuid": "ab676553-b602-441f-a38d-f1296eff6537", + "mavenUrls": [ + "http://devsite.ctr-electronics.com/maven/release/" + ], + "jsonUrl": "http://devsite.ctr-electronics.com/maven/release/com/ctre/phoenix/Phoenix-latest.json", + "javaDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-java", + "version": "5.12.0" + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-java", + "version": "5.12.0" + } + ], + "jniDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.12.0", + "isJar": false, + "skipInvalidPlatforms": true, + "validPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + } + ], + "cppDependencies": [ + { + "groupId": "com.ctre.phoenix", + "artifactId": "wpiapi-cpp", + "version": "5.12.0", + "libName": "CTRE_Phoenix_WPI", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "api-cpp", + "version": "5.12.0", + "libName": "CTRE_Phoenix", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "cci", + "version": "5.12.0", + "libName": "CTRE_PhoenixCCI", + "headerClassifier": "headers", + "sharedLibrary": false, + "skipInvalidPlatforms": true, + "binaryPlatforms": [ + "linuxathena", + "windowsx86-64", + "linuxx86-64" + ] + }, + { + "groupId": "com.ctre.phoenix", + "artifactId": "core", + "version": "5.12.0", + "libName": "CTRE_PhoenixCore", + "headerClassifier": "headers" + } + ] +} From c21f9d10db4c08be4d1ea06f1affd6065e2bf15a Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 00:50:12 -0800 Subject: [PATCH 06/29] Make travis use Java 11 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2352728f..f05ab994 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: java +jdk: + - openjdk11 before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ From 3a78dd290d019b9323fa457dcd858d0cfc3b5a34 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 00:56:37 -0800 Subject: [PATCH 07/29] Add permwrapper directory to travis cache GradleRIO changes this to prevent Travis from deleting the wrapper distribution. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index f05ab994..417c2421 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ cache: directories: - "$HOME/.gradle/caches/" - "$HOME/.gradle/wrapper/" + - "$HOME/.gradle/permwrapper/" - "$HOME/.gradle/gradlerio/" notifications: slack: From d68aeda24c0f80f2daef53fe4330ed75af4a8d6a Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 14:11:24 -0800 Subject: [PATCH 08/29] Apply Kotlin plugin with plugins DSL --- build.gradle | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 0acbe3e3..fba7ea5e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,10 @@ -buildscript { - ext.kotlin_version = '1.3.0' - - repositories { - mavenCentral() - } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - plugins { id "java" id "maven" id "edu.wpi.first.GradleRIO" version "2019.1.1" + id "org.jetbrains.kotlin.jvm" version "1.3.0" } -apply plugin: "kotlin" - task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource @@ -63,7 +50,7 @@ dependencies { // Non-FRC depedencies compile 'org.jetbrains:annotations:16.0.3' - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.0" compile "com.google.guava:guava:27.0-jre" compile "org.apache.commons:commons-math3:3.6.1" From 0ebc5fa5e9436713e97ece2f2546826edf7df0e4 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 15:53:13 -0800 Subject: [PATCH 09/29] Add robot deployment and make sim work There's a bit of an ugly workaround with simulateJava's dependencies (see wpilibsuite/GradleRIO#283) and the robot it's deploying doesn't entirely exist yet. --- build.gradle | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fba7ea5e..3fa5761b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import edu.wpi.first.gradlerio.GradleRIOPlugin + plugins { id "java" id "maven" @@ -95,14 +97,20 @@ jar.dependsOn(compileTestbotsJava) jar { inputs.files(compileTestbotsJava.outputs.files) gradle.taskGraph.whenReady { - if (gradle.taskGraph.hasTask(":deploy")) { // fully qualified task name is needed + if (gradle.taskGraph.hasTask(":deployFrcJava") || gradle.taskGraph.hasTask(":simulateJava") || gradle.taskGraph.hasTask(":simulateExternalJava")) { // fully qualified task name is needed + System.out.println("Deployment or simulation detected.") from ((configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) + sourceSets.testbots.output) + manifest GradleRIOPlugin.javaManifest("org.team1540.rooster.testing.TestbotLoaderMain") } } } +simulateJava.dependsOn.removeAll() +simulateJava.dependsOn(extractTestJNI) +simulateJava.dependsOn(jar) + wrapper { gradleVersion = '5.0' distributionType = Wrapper.DistributionType.ALL From 0a0f453317ad11767c5217e0632a7f821f01479f Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 15:59:00 -0800 Subject: [PATCH 10/29] Make everything use TimedRobot --- .../org/team1540/rooster/testing/AdjustableTestRobot.java | 4 ++-- .../org/team1540/rooster/testing/AsyncCommandTestRobot.java | 4 ++-- .../java/org/team1540/rooster/testing/BlinkenTestRobot.java | 4 ++-- .../java/org/team1540/rooster/testing/DriveTestRobot.java | 4 ++-- .../org/team1540/rooster/testing/HelloWorldTestRobot.java | 5 +++-- .../org/team1540/rooster/testing/PreferencesTestRobot.java | 4 ++-- .../org/team1540/rooster/testing/DrivePipelineTesting.kt | 2 +- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java index 6075f614..dce54f64 100644 --- a/src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/AdjustableTestRobot.java @@ -1,8 +1,8 @@ package org.team1540.rooster.testing; -import edu.wpi.first.wpilibj.IterativeRobot; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.Solenoid; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.command.Scheduler; import org.team1540.rooster.adjustables.AdjustableManager; import org.team1540.rooster.adjustables.Telemetry; @@ -10,7 +10,7 @@ import org.team1540.rooster.power.PowerManager; @Deprecated -public class AdjustableTestRobot extends IterativeRobot { +public class AdjustableTestRobot extends TimedRobot { @Tunable("A boolean") public boolean b; diff --git a/src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java index 9badf295..5b1c7a83 100644 --- a/src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/AsyncCommandTestRobot.java @@ -1,6 +1,6 @@ package org.team1540.rooster.testing; -import edu.wpi.first.wpilibj.IterativeRobot; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.Timer; import edu.wpi.first.wpilibj.command.Command; import edu.wpi.first.wpilibj.command.Scheduler; @@ -8,7 +8,7 @@ import org.team1540.rooster.preferencemanager.Preference; import org.team1540.rooster.util.AsyncCommand; -public class AsyncCommandTestRobot extends IterativeRobot { +public class AsyncCommandTestRobot extends TimedRobot { Timer toggleTimer = new Timer(); Command command1 = new NonAsyncTest(); diff --git a/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java index 81e06b9e..4887e18a 100644 --- a/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/BlinkenTestRobot.java @@ -1,12 +1,12 @@ package org.team1540.rooster.testing; -import edu.wpi.first.wpilibj.IterativeRobot; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import org.team1540.rooster.wrappers.RevBlinken; import org.team1540.rooster.wrappers.RevBlinken.ColorPattern; -public class BlinkenTestRobot extends IterativeRobot { +public class BlinkenTestRobot extends TimedRobot { private SendableChooser patternChooser = new SendableChooser<>(); private int currentID = -1; diff --git a/src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java index bf253025..da1aace5 100644 --- a/src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/DriveTestRobot.java @@ -3,13 +3,13 @@ import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix.motorcontrol.FeedbackDevice; import edu.wpi.first.wpilibj.Compressor; -import edu.wpi.first.wpilibj.IterativeRobot; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.Solenoid; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import org.team1540.rooster.wrappers.ChickenTalon; -public class DriveTestRobot extends IterativeRobot { +public class DriveTestRobot extends TimedRobot { Compressor compressor = new Compressor(); Joystick joystick; ChickenTalon lMaster; diff --git a/src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java index 975b3c40..81fa479a 100644 --- a/src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/HelloWorldTestRobot.java @@ -1,9 +1,10 @@ package org.team1540.rooster.testing; -import edu.wpi.first.wpilibj.IterativeRobot; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; -public class HelloWorldTestRobot extends IterativeRobot { +public class HelloWorldTestRobot extends TimedRobot { + @Override public void robotInit() { String debugString = "debug"; diff --git a/src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java b/src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java index a20a8f76..f9f0d8e0 100644 --- a/src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java +++ b/src/testbots/java/org/team1540/rooster/testing/PreferencesTestRobot.java @@ -1,14 +1,14 @@ package org.team1540.rooster.testing; -import edu.wpi.first.wpilibj.IterativeRobot; import edu.wpi.first.wpilibj.Notifier; +import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.command.Scheduler; import org.team1540.rooster.power.PowerManager; import org.team1540.rooster.preferencemanager.Preference; import org.team1540.rooster.preferencemanager.PreferenceManager; -public class PreferencesTestRobot extends IterativeRobot { +public class PreferencesTestRobot extends TimedRobot { @Preference("A boolean") public boolean b; diff --git a/src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt b/src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt index 0321f531..30ce4886 100644 --- a/src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt +++ b/src/testbots/kotlin/org/team1540/rooster/testing/DrivePipelineTesting.kt @@ -21,7 +21,7 @@ import java.util.function.DoubleSupplier * Base class that all other testing classes inherit from; just has a command that gets started when * teleop starts. */ -abstract class DrivePipelineTestRobot : IterativeRobot() { +abstract class DrivePipelineTestRobot : TimedRobot() { protected abstract val command: Command override fun teleopInit() { From f89a63373ca4158dab29d207e61c84f227d9852d Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 20:45:55 -0800 Subject: [PATCH 11/29] Re-add run configuration robot class deployment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WPILib doesn’t specify the robot class in the manifest anymore (you instead define a main function that instantiates your robot of choice), so the run-configuration-robot-class-thing broke. This is a really ugly fix. According to WPILib, you aren't supposed to modify the main function at all. Therefore, I thought it prudent to add roughly 70 lines of code doing all sorts of interesting filesystem and reflection things. Basically, now what this does is the custom createRobotclassTxt task reads the robotClass project property (which used to be passed into GradleRIO) and writes it to a text file (specifically, build/robotclass.txt). GradleRIO then deploys robotclass.txt to the robot, where TestbotLoaderMain (which is the "main class" for all ROOSTER test robots) reads it and attempts to instantiate the class named within. Now, while I concede this is a terrible system, it is only marginally worse than what WPILib/GradleRIO used to be doing (namely, putting the robot class in the jar manifest and reading that.) So I think that I'm just being ingenious. --- build.gradle | 26 ++++++ .../rooster/testing/TestbotLoaderMain.java | 82 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java diff --git a/build.gradle b/build.gradle index 3fa5761b..72f0b23f 100644 --- a/build.gradle +++ b/build.gradle @@ -72,6 +72,20 @@ configurations { testbotsRuntimeOnly.extendsFrom runtimeOnly } +// task to create robotclass.txt +task createRobotclassTxt { + inputs.property("robotClass", project.hasProperty("robotClass") ? project.robotClass : "") + outputs.files("$buildDir/robotclass.txt") + doLast { + if (!project.hasProperty("robotClass")) { + throw new GradleException("Robot class not set. Pass a value in on the command line by adding -ProbotClass=.") + } else { + System.out.println("Using robot class " + project.robotClass) + new File("$buildDir/robotclass.txt").text = project.robotClass + } + } +} + deploy { targets { roboRIO("roborio") { @@ -89,6 +103,18 @@ deploy { */ debug = project.hasProperty("deploy-debug") } + // This is a file (generated by the task above) that contains the name of the robot class we want to deploy. + // The file then gets read by TestbotLoaderMain and it instantiates the class using + // reflection. + // TODO: Maybe a build-time check that said class file actually exists somewhere in the project? + fileArtifact('robotclassTxt') { + targets << "roborio" + + file = file("$buildDir/robotclass.txt") + directory = "/home/lvuser/" + + dependsOn(createRobotclassTxt) + } } } diff --git a/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java b/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java new file mode 100644 index 00000000..8eaf0fda --- /dev/null +++ b/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java @@ -0,0 +1,82 @@ +package org.team1540.rooster.testing; + +import edu.wpi.first.wpilibj.RobotBase; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +public class TestbotLoaderMain { + + public static void main(String... args) { + // workaround for GradleRIO simulation throwing away standard error + if (RobotBase.isSimulation()) { + System.setErr(System.out); + } + + // try to find a robotclass.txt file + File robotClassFile = new File("robotclass.txt"); + + if (robotClassFile.isFile()) { + // attempt to read from the file + try (BufferedReader reader = new BufferedReader(new FileReader(robotClassFile))) { + String robotClass = reader.readLine(); + if (robotClass == null) { + System.err.println( + "robotclass.txt is empty. Make sure your deployment is correctly configured."); + } + System.out.println("Attempting to start robot class " + robotClass); + + RobotBase.startRobot(() -> { + try { + return Class.forName(robotClass).asSubclass(RobotBase.class).getDeclaredConstructor() + .newInstance(); + } catch (ClassNotFoundException e) { + System.err.println("Could not find robot class " + robotClass + + ". Check for typos in the provided class name."); + throw new RuntimeException(e); + } catch (ClassCastException e) { + System.err.println("Class " + robotClass + + " is not a subclass of RobotBase. Make sure your robot class is set up correctly."); + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + System.err.println("Class " + robotClass + + " does not have a no-argument constructor. Make sure your robot class is set up correctly."); + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + System.err.println("The no-argument constructor for class " + robotClass + + " is private; change it to public."); + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + System.err.println( + "An IllegalArgumentException was somehow thrown by a method that takes no arguments. Make sure your robot is sane."); + throw new RuntimeException(e); + } catch (InstantiationException e) { + System.err.println( + "The provided robot class is abstract and cannot be instantiated. Make sure your robot class is set up correctly."); + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + System.err.println("Your robot threw an exception (" + e.getCause().toString() + + ") while being instantiated. Check your constructor (if applicable) and field initializers."); + throw new RuntimeException(e); + } + }); + + } catch (FileNotFoundException e) { + System.err.println( + "Could not find robotclass.txt file. Make sure your deployment is correctly configured."); + throw new RuntimeException(e); + } catch (IOException e) { + System.err.println( + "IOException while reading robotclass.txt file. If this appears repeatedly, something has gone terribly wrong."); + throw new RuntimeException(e); + } + } else { + System.err.println( + "Could not find robotclass.txt file. Make sure your deployment is correctly configured."); + System.exit(1); + } + } +} From 96e49bbcefeeda41be0f3febc5d50e1ec4d3fd6b Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 20:46:24 -0800 Subject: [PATCH 12/29] Reorganize/label build file --- build.gradle | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 72f0b23f..f84c2c79 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,8 @@ plugins { id "org.jetbrains.kotlin.jvm" version "1.3.0" } + +// Maven/Jitpack configuration task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource @@ -22,14 +24,6 @@ artifacts { archives javadocJar } -repositories { - mavenCentral() - maven { - name "JitPack" - url "https://jitpack.io/" - } -} - javadoc { options { header '' @@ -42,6 +36,15 @@ javadoc { options.addBooleanOption('Xdoclint:all,-missing', true) } +// Dependencies + +repositories { + mavenCentral() + maven { + name "JitPack" + url "https://jitpack.io/" + } +} dependencies { // FRC dependencies @@ -50,7 +53,7 @@ dependencies { nativeZip wpi.deps.vendor.jni(wpi.platforms.roborio) nativeDesktopZip wpi.deps.vendor.jni(wpi.platforms.desktop) - // Non-FRC depedencies + // Non-FRC dependencies compile 'org.jetbrains:annotations:16.0.3' compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.0" @@ -60,6 +63,9 @@ dependencies { compile "com.github.oxo42:stateless4j:3dd512049f" } +// deployment and GradleRIO + +// define the testbots source set sourceSets { testbots { compileClasspath += sourceSets.main.output @@ -123,9 +129,10 @@ jar.dependsOn(compileTestbotsJava) jar { inputs.files(compileTestbotsJava.outputs.files) gradle.taskGraph.whenReady { - if (gradle.taskGraph.hasTask(":deployFrcJava") || gradle.taskGraph.hasTask(":simulateJava") || gradle.taskGraph.hasTask(":simulateExternalJava")) { // fully qualified task name is needed + if (gradle.taskGraph.hasTask(":deployFrcJava") || gradle.taskGraph.hasTask(":simulateJava") || gradle.taskGraph.hasTask(":simulateExternalJava")) { + // fully qualified task name is needed System.out.println("Deployment or simulation detected.") - from ((configurations.compile.collect { + from((configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) + sourceSets.testbots.output) manifest GradleRIOPlugin.javaManifest("org.team1540.rooster.testing.TestbotLoaderMain") @@ -137,6 +144,8 @@ simulateJava.dependsOn.removeAll() simulateJava.dependsOn(extractTestJNI) simulateJava.dependsOn(jar) +// wrapper + wrapper { gradleVersion = '5.0' distributionType = Wrapper.DistributionType.ALL From d3db4e2324b89cc6966048012376be369eaea647 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 20:50:56 -0800 Subject: [PATCH 13/29] Document TestbotLoaderMain --- .../org/team1540/rooster/testing/TestbotLoaderMain.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java b/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java index 8eaf0fda..a5ad73a6 100644 --- a/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java +++ b/src/testbots/java/org/team1540/rooster/testing/TestbotLoaderMain.java @@ -8,6 +8,12 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; +/** + * Class to load test robots. This class reads a robotclass.txt file from the RoboRIO filesystem and + * uses that to decide which robot to instantiate. This is so that the robot class can be specified + * at build time (by modifying a robotclass.txt to be deployed to the RIO) rather than compile + * time. + */ public class TestbotLoaderMain { public static void main(String... args) { From daca40c701e3394824406d1473293d7348875edc Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Fri, 4 Jan 2019 21:08:48 -0800 Subject: [PATCH 14/29] Remove CTRE javadoc links Because they took those down for some reason. --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f84c2c79..82028319 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ artifacts { javadoc { options { header '' - links 'http://first.wpi.edu/FRC/roborio/release/docs/java/', 'http://www.ctr-electronics.com/downloads/api/java/html/' + links 'http://first.wpi.edu/FRC/roborio/release/docs/java/' linksOffline('https://docs.oracle.com/javase/8/docs/api/', 'https://docs.oracle.com/javase/8/docs/api/') } options.addStringOption("doctitle", "ROOSTER API") @@ -110,7 +110,7 @@ deploy { debug = project.hasProperty("deploy-debug") } // This is a file (generated by the task above) that contains the name of the robot class we want to deploy. - // The file then gets read by TestbotLoaderMain and it instantiates the class using + // The file then gets read by TestbotLoaderMain and it instantiates the class using // reflection. // TODO: Maybe a build-time check that said class file actually exists somewhere in the project? fileArtifact('robotclassTxt') { From 73ac579494e910e098bfbc6c39ed57852d47bf54 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Sat, 5 Jan 2019 19:31:47 -0800 Subject: [PATCH 15/29] Minor Javadoc update Co-Authored-By: edelmanjm --- .../team1540/rooster/testers/motor/SimpleControllersTester.java | 2 +- 1 file changed, 1 insertion(+), 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 055f0de4..6a5aec02 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 @@ -149,7 +149,7 @@ public void initSendable(SendableBuilder builder) { /** * Add all the sendables to the {@link SmartDashboard}. Includes chooser for selecting the * active {@link IMotorController} and information about the tester. Follows a builder pattern. - * @return Follows a builder pattern. + * @return This {@code SimpleControllersTester} in a builder pattern. */ public SimpleControllersTester addAllSendables() { SmartDashboard.putData("Controller tester info", this); From 7ed1dfec3a296200cea7b88ea539e5059b50a8a3 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Sat, 5 Jan 2019 19:33:00 -0800 Subject: [PATCH 16/29] Add contract, suppress warning --- .../rooster/testers/motor/SimpleControllersTester.java | 3 +++ 1 file changed, 3 insertions(+) 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 6a5aec02..b5d6cfdb 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 @@ -13,6 +13,7 @@ import java.util.NavigableMap; import java.util.Optional; import java.util.TreeMap; +import org.jetbrains.annotations.Contract; import org.team1540.rooster.util.SimpleCommand; /** @@ -151,6 +152,8 @@ public void initSendable(SendableBuilder builder) { * active {@link IMotorController} and information about the tester. Follows a builder pattern. * @return This {@code SimpleControllersTester} in a builder pattern. */ + @SuppressWarnings("UnusedReturnValue") + @Contract(" -> this") public SimpleControllersTester addAllSendables() { SmartDashboard.putData("Controller tester info", this); SmartDashboard.putData("Controller choose", getControllerChooser()); From 54f1f3a7cbc93d3f575f633f72338f7153e4dd08 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Sun, 6 Jan 2019 00:18:09 -0800 Subject: [PATCH 17/29] Revise project structure section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 007ccd9e..a806ede3 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ _Note: If you need to use changes that have just been pushed to master, you may ### Project Structure -ROOSTER is made up of two separate Gradle projects, `lib` (the actual ROOSTER library) and `test` (self-contained robot classes to test library components). Anything in `lib` gets packaged into the distribution JARs, while `test` is only for testing the components from `lib` (and as such includes GradleRIO etc. for deploying to a robot). +ROOSTER's code is divided into two segments: `main` (in `src/main`), containing main library code which is packed into distribution JARs and given to anyone who adds the library as a dependency, and `testbots` (in `src/testbots`), containing robot classes etc. for testing the components in `main`. ### Building From da0c9086470a7201f1d2614674074e6b338e19b7 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Sun, 6 Jan 2019 00:24:37 -0800 Subject: [PATCH 18/29] Update NavX dependency JSON --- vendordeps/{NavX.json => navx_frc.json} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename vendordeps/{NavX.json => navx_frc.json} (86%) diff --git a/vendordeps/NavX.json b/vendordeps/navx_frc.json similarity index 86% rename from vendordeps/NavX.json rename to vendordeps/navx_frc.json index 173fb45f..255358b6 100644 --- a/vendordeps/NavX.json +++ b/vendordeps/navx_frc.json @@ -1,7 +1,7 @@ { - "fileName": "NavX.json", + "fileName": "navx_frc.json", "name": "KauaiLabs_navX_FRC", - "version": "3.1.339", + "version": "3.1.344", "uuid": "cb311d09-36e9-4143-a032-55bb2b94443b", "mavenUrls": [ "https://repo1.maven.org/maven2/" @@ -11,7 +11,7 @@ { "groupId": "com.kauailabs.navx.frc", "artifactId": "navx-java", - "version": "3.1.339" + "version": "3.1.344" } ], "jniDependencies": [], @@ -19,7 +19,7 @@ { "groupId": "com.kauailabs.navx.frc", "artifactId": "navx-cpp", - "version": "3.1.339", + "version": "3.1.344", "headerClassifier": "headers", "sourcesClassifier": "sources", "sharedLibrary": false, From edc0d8d30fdcb8bcc87a916902dd0b3520a7839f Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Sun, 6 Jan 2019 00:28:27 -0800 Subject: [PATCH 19/29] Add section about dependency JSONs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a806ede3..4b61d2c0 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,8 @@ dependencies { } ``` +Additionally, you should be using the latest version of GradleRIO with CTRE Phoenix, Kauai Labs NavX, and Pathfinder v1 vendor libraries installed. + We use [JitPack](https://jitpack.io) as a Gradle/Maven repository. This means that if you add the project using Gradle it will be automatically updated with the latest changes to the `master` branch, as well as source code and documentation .jar files. Using `master-SNAPSHOT` as a version number is good for projects you're actively developing, but after you've finished it's better to anchor it to a specific version (simply change "`master-SNAPSHOT`" to the version number) to avoid possible backwards-compatibility issues. From f2a750ea1b9bcf62b59817c1e7a359d258f927f2 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Sun, 6 Jan 2019 14:30:33 -0800 Subject: [PATCH 20/29] Re-add testers RIP git history and blame. Thanks Zachary for not deleting them in a separate commit. See 2e75ee5. Command used was `git checkout -p 2e75ee5^ -- lib/src/main/java/org/team1540/rooster/testers/*.java` --- .../rooster/testers/AbstractTester.java | 209 ++++++++++++++++++ .../rooster/testers/ResultWithMetadata.java | 32 +++ .../org/team1540/rooster/testers/Tester.java | 91 ++++++++ .../rooster/testers/motor/BurnoutTester.java | 174 +++++++++++++++ .../testers/motor/ControllersMultiTester.java | 207 +++++++++++++++++ .../rooster/testers/motor/EncoderTester.java | 145 ++++++++++++ .../motor/SimpleControllersTester.java | 159 +++++++++++++ .../rooster/testers/package-info.java | 4 + 8 files changed, 1021 insertions(+) create mode 100644 lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/Tester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java create mode 100644 lib/src/main/java/org/team1540/rooster/testers/package-info.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java new file mode 100644 index 00000000..56eabb27 --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java @@ -0,0 +1,209 @@ +package org.team1540.rooster.testers; + +import com.google.common.collect.EvictingQueue; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A basic implementation of the Tester interface using essentially EvictingQueues for result + * storage. + */ +@SuppressWarnings({"unused", "UnstableApiUsage"}) +public abstract class AbstractTester implements Tester { + + private static float DEFAULT_LOG_TIME = 150; + private static int DEFAULT_UPDATE_DELAY = 2500; + + private int updateDelay; + private boolean running = true; + @NotNull + private List itemsToTest; + @NotNull + private Function test; + @Nullable + private List> runConditions; + @NotNull + private Map> storedResults; + + /** + * Construct a new instance with the default queue depth. + * + * @param test The test to execute. + * @param itemsToTest The items to apply the test to. + * @param runConditions The conditions that must be met before the test will be executed on an + * item. + */ + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + @Nullable List> runConditions) { + this(test, itemsToTest, runConditions, (int) DEFAULT_LOG_TIME / (DEFAULT_UPDATE_DELAY / 1000)); + } + + /** + * Construct a new instance, specifying the logTime and updateDelay to calculate the queue depth. + * + * @param test The test to execute. + * @param itemsToTest The items to apply the test to. + * @param runConditions The conditions that must be met before the test will be executed on an + * item. + * @param logTime The maximum length of time for which we want to store the results. Note that + * this is just used for estimating the queue depth based on the update delay, not actually + * checked against while running. + * @param updateDelay The delay between the test being run on the items. + */ + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + @Nullable List> runConditions, float logTime, int updateDelay) { + this(test, itemsToTest, runConditions, + (int) (logTime / ((float) updateDelay / 1000f))); + this.updateDelay = updateDelay; + } + + /** + * + * Construct a new instance with a given queueDepth. + * @param test The test to execute. + * @param itemsToTest The items to apply the test to. + * @param runConditions The conditions that must be met before the test will be executed on an + * item. + * @param queueDepth The maximum number of items that the {@link EvictingQueue} can hold. + */ + @SuppressWarnings("WeakerAccess") + protected AbstractTester(@NotNull Function test, @NotNull List itemsToTest, + @Nullable List> runConditions, int queueDepth) { + this.test = test; + this.itemsToTest = itemsToTest; + this.runConditions = runConditions; + this.storedResults = new HashMap<>(itemsToTest.size()); + // TODO I feel like there's a cleaner way of doing this + for (T t : itemsToTest) { + this.storedResults.put(t, new ResultStorage<>(queueDepth)); + } + } + + @Override + @NotNull + public Function getTest() { + return test; + } + + @Override + public void setTest(@NotNull Function test) { + this.test = test; + } + + @Override + @Nullable + public List> getRunConditions() { + return runConditions; + } + + @Override + public void setRunConditions( + @Nullable List> runConditions) { + this.runConditions = runConditions; + } + + + @Override + @NotNull + public List getItemsToTest() { + return Collections.unmodifiableList(itemsToTest); + } + + @Override + @NotNull + public EvictingQueue> getStoredResults(T key) { + return storedResults.get(key).queuedResults; + } + + @Override + @Nullable + public ResultWithMetadata peekMostRecentResult(T key) { + return storedResults.get(key).lastResult; + } + + @Override + public int getUpdateDelay() { + return updateDelay; + } + + @Override + public int setUpdateDelay(int delay) { + int oldUpdateDelay = this.updateDelay; + this.updateDelay = delay; + return oldUpdateDelay; + } + + @Override + public boolean setRunning(boolean status) { + boolean oldRunning = this.running; + this.running = status; + return 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!) + */ + protected void periodic() { + for (T t : itemsToTest) { + // 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()); + } + } + + @Override + public void run() { + while (true) { + + if (running) { + periodic(); + } + + try { + Thread.sleep(updateDelay); + } catch (InterruptedException e) { + // End the thread + return; + } + } + } + + /** + * A class for handling the storage of results. Basically just so the tail can actually be + * peeked at. + * @param The returned type to store. + */ + private class ResultStorage { + + @Nullable + private ResultWithMetadata lastResult; + @NotNull + private EvictingQueue> queuedResults; + + private ResultStorage(int queueDepth) { + this.queuedResults = EvictingQueue.create(queueDepth); + } + + private void addResult(A result, long timeStampMillis) { + ResultWithMetadata resultWithMetadata = new ResultWithMetadata<>(result, timeStampMillis); + this.lastResult = resultWithMetadata; + queuedResults.add(resultWithMetadata); + } + + } + +} diff --git a/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java b/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java new file mode 100644 index 00000000..ef0be5ed --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java @@ -0,0 +1,32 @@ +package org.team1540.rooster.testers; + +import org.jetbrains.annotations.NotNull; + +/** + * Class for encapsulating results. Includes the result and a timestamp. + * + * @param The result type. + */ +public class ResultWithMetadata { + + @NotNull + private final R result; + private final long timestampMillis; + + ResultWithMetadata(@NotNull R result, long timestampMillis) { + this.result = result; + this.timestampMillis = timestampMillis; + } + + @SuppressWarnings("WeakerAccess") + @NotNull + public R getResult() { + return result; + } + + @SuppressWarnings("unused") + public long getTimestampMillis() { + return timestampMillis; + } + +} diff --git a/lib/src/main/java/org/team1540/rooster/testers/Tester.java b/lib/src/main/java/org/team1540/rooster/testers/Tester.java new file mode 100644 index 00000000..51e98dae --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/Tester.java @@ -0,0 +1,91 @@ +package org.team1540.rooster.testers; + +import com.google.common.collect.EvictingQueue; +import java.util.List; +import java.util.function.Function; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * An interface for testing things. + * + * @param The type of item being tested. + * @param The return type of the test results. + */ +@SuppressWarnings("unused") +public interface Tester extends Runnable { + + @NotNull + Function getTest(); + + void setTest(@NotNull Function tests); + + /** + * Gets the conditions that must be met before the test will be executed on an item. + * + * @return An potentially {@link List} of the run conditions, or null if there are none (always + * execute.) + */ + @SuppressWarnings("UnstableApiUsage") + @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. + * + * @return An {@link EvictingQueue} of the items that are being tested. + */ + @SuppressWarnings("UnstableApiUsage") + @NotNull // TODO how to annotate as unmodifiable? + List getItemsToTest(); + + /** + * Get the results of the test being run. + * + * @param key The item to get the results for. + * @return An {@link EvictingQueue} of {@link ResultWithMetadata} that encapsulate the returned + * values. + */ + @SuppressWarnings("UnstableApiUsage") + @NotNull + EvictingQueue> getStoredResults(T key); + + /** + * Gets the most recent result of the the test without impacting the stored results. + * + * @param key The item to get the results for. + * @return A {@link ResultWithMetadata} that encapsulate the returned value. + */ + @Nullable + ResultWithMetadata peekMostRecentResult(T key); + + /** + * Gets the delay between the test being run on the items. + * @return The delay in milliseconds. + */ + int getUpdateDelay(); + + /** + * Set the delay between the test being run on the items. + * @param delay The new delay in milliseconds. + * @return The previous delay in milliseconds. + */ + @SuppressWarnings("UnusedReturnValue") + int setUpdateDelay(int delay); + + /** + * Set if the tests should be running. Note that this does not stop the thread, only the actual + * execution of the tests. + * @param status The new status. + * @return The old status. + */ + boolean setRunning(boolean status); +} 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 new file mode 100644 index 00000000..9ae39f9d --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -0,0 +1,174 @@ +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; +import java.util.List; +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 + * motors and reporting low outliers or checking if a single motor is below the cutoff. + */ +@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; + 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, 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. + */ + @SuppressWarnings("WeakerAccess") + public BurnoutTester(IMotorController... motorsToTest) { + this(Arrays.asList(motorsToTest)); + } + + /** + * 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. + */ + @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, null, 150, 500); + this.setTest(motorsToTest.size() > 2 ? this::testBurnoutMultiMotor : + this::testBurnoutSingleMotor); + } + + /** + * 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 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 + protected void periodic() { + double[] currents = getItemsToTest().stream().mapToDouble(IMotorController::getOutputCurrent) + .toArray(); + medianCurrent = medianCalculator.evaluate(currents); + stdDevCurrent = stdDevCalculator.evaluate(currents); + super.periodic(); + } + + @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 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. + */ + @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 + 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); + } + builder.addDoubleProperty("Median current", () -> medianCurrent, null); + } +} 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..14f12d0e --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -0,0 +1,207 @@ +package org.team1540.rooster.testers.motor; + +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; +import edu.wpi.first.wpilibj.Timer; +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; + +/** + * 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(); + private StateMachine stateMachine; + 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) + .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).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(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); + this.stateMachine.setShouldLog(false); + } + + /** + * 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) { + tests.add(new TesterAndCommand(new BurnoutTester(controllerGroup), function)); + 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) { + tests.add(new TesterAndCommand(new EncoderTester(controllerGroup), function)); + return this; + } + + @Override + protected void initialize() { + timer.reset(); + 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 (TesterAndCommand testerAndCommand : tests) { + for (IMotorController controller : testerAndCommand.getTest().getItemsToTest()) { + int failureCount = 0; + for (ResultWithMetadata result : testerAndCommand.getTest() + .getStoredResults(controller)) { + if (result.getResult().equals(Boolean.TRUE)) { + failureCount++; + } + } + if (failureCount > 0) { + DriverStation + .reportError("Motor " + controller.getDeviceID() + " reported " + failureCount + + " failures of type " + testerAndCommand.getTest(), false); + } + } + } + System.out.println("Finished testing"); + } + + @Override + 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 { + INITIALIZING(0), SPIN_UP(0.25), EXECUTING(1), SPIN_DOWN(0), 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 + } + + 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; + } + } + +} 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 new file mode 100644 index 00000000..f3dcc8e0 --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -0,0 +1,145 @@ +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; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +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 + * 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 IMotorController IMotorControllers} to compare to each other. + */ + @SuppressWarnings("WeakerAccess") + 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 IMotorController IMotorControllers} 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 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 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 + && Math.abs(controller.getSelectedSensorVelocity(0)) < 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 encoder 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 encoder 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 IMotorController}. + * + * @param builder The {@link SendableBuilder} to use. + */ + @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)); + if (result.isPresent()) { + return result.get().getResult(); + } else { + return false; + } + }, null); + } + } +} 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 new file mode 100644 index 00000000..95cc63d0 --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java @@ -0,0 +1,159 @@ +package org.team1540.rooster.testers.motor; + +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; +import edu.wpi.first.wpilibj.command.Command; +import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; +import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; +import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.TreeMap; +import org.team1540.rooster.util.SimpleCommand; + +/** + * A simple command for testing a series of {@link IMotorController IMotorControllers}. Simply + * add the controllers, specify control bindings or use the defaults, and you're ready! Select + * the motor in the SmartDashboard or move next/previous with buttons and control the percent + * output of the motor with a joystick axis. + */ +public class SimpleControllersTester extends Command implements Sendable { + + private static final int DEFAULT_JOYSTICK_ID = 0; + private static final int DEFAULT_AXIS_ID = 5; + private static final int DEFAULT_NEXT_BUTTON_ID = 1; + private static final int DEFAULT_PREVIOUS_BUTTON_ID = 2; + + private Joystick joystick; + private int axisId; + + // Is the retrieval time poor, despite the need for a lot of retrievals? Yes. Are we ignoring that + // because the size of the map is small? Also yes. + private NavigableMap controllers = new TreeMap<>(); + + // Do not manually update this value. Instead, call setCurrentController() + private IMotorController currentController; + + private SendableChooser controllerChooser = new SendableChooser<>(); + + /** + * Construct a new instance with the default bindings on {@link Joystick} 0. On an Xbox + * controller, the right thumbstick y-axis controls {@link ControlMode}.PERCENT_OUTPUT, the A + * button goes to the next {@link IMotorController}, and the B button goes to the previous + * {@link IMotorController}. + * + * @param controllers The {@link IMotorController IMotorControllers} to test. + */ + public SimpleControllersTester(IMotorController... controllers) { + this(new Joystick(DEFAULT_JOYSTICK_ID), DEFAULT_AXIS_ID, DEFAULT_NEXT_BUTTON_ID, + DEFAULT_PREVIOUS_BUTTON_ID, controllers); + } + + /** + * Construct a new instance with the specified bindings. + * @param joystick The joystick port to use. + * @param axisId The axis on the joystick to use. + * @param nextButtonId The button to use to proceed to the next {@link IMotorController}. + * @param previousButtonId The button to use to proceed to the nex {@link IMotorController}. + * @param controllers The {@link IMotorController IMotorControllers} to test. + */ + @SuppressWarnings("WeakerAccess") + public SimpleControllersTester(Joystick joystick, int axisId, int nextButtonId, + int previousButtonId, + IMotorController... controllers) { + this.joystick = joystick; + this.axisId = axisId; + + // Make the buttons go the next and previous controller, or loop around if not available + new JoystickButton(this.joystick, nextButtonId).whenPressed(new SimpleCommand("Next " + + "controller ID", + () -> setCurrentController( + Optional.ofNullable(this.controllers.higherKey(this.currentController.getDeviceID())) + .orElse(this.controllers.firstKey())))); + new JoystickButton(this.joystick, previousButtonId).whenPressed(new SimpleCommand("Previous " + + "controller ID", + () -> setCurrentController( + Optional.ofNullable(this.controllers.lowerKey(this.currentController.getDeviceID())) + .orElse(this.controllers.lastKey())))); + + // Add a chooser that you can use to select the controller and initialize it to null, + // corresponding with buttons should be used + this.controllerChooser.addObject("Use buttons", null); + for (IMotorController controller : controllers) { + 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()); + } + + /** + * Checks every tick if the chooser is set to a controller or to use buttons, then sets the + * output according to the value of the active joystick. + */ + @Override + protected void execute() { + if (controllerChooser.getSelected() != null) { + setCurrentController(controllerChooser.getSelected()); + } + currentController.set(ControlMode.PercentOutput, joystick.getRawAxis(axisId)); + } + + /** + * Updates the currently active {@link IMotorController}. + * @param newId The ID of the new {@link IMotorController}. + */ + @SuppressWarnings("WeakerAccess") + public void setCurrentController(int newId) { + if (currentController != null) { + currentController.set(ControlMode.PercentOutput, 0); + } + currentController = controllers.get(newId); + } + + /** + * Prevents the command from finishing. + * @return false. + */ + @Override + protected boolean isFinished() { + return false; + } + + /** + * Gets the chooser used for selecting the current {@link IMotorController}. + * @return A chooser. + */ + @SuppressWarnings("WeakerAccess") + public SendableChooser getControllerChooser() { + return controllerChooser; + } + + /** + * Adds a single property showing the current active controller. + * @param builder The thing to add the things to. + */ + @Override + public void initSendable(SendableBuilder builder) { + builder.addDoubleProperty("Controller ID", () -> this.currentController.getDeviceID(), + (id) -> setCurrentController((int) id)); + } + + /** + * Add all the sendables to the {@link SmartDashboard}. Includes chooser for selecting the + * active {@link IMotorController} and information about the tester. + */ + public void addAllSendables() { + SmartDashboard.putData("Controller tester info", this); + SmartDashboard.putData("Controller choose", getControllerChooser()); + } + + +} diff --git a/lib/src/main/java/org/team1540/rooster/testers/package-info.java b/lib/src/main/java/org/team1540/rooster/testers/package-info.java new file mode 100644 index 00000000..33dbb24a --- /dev/null +++ b/lib/src/main/java/org/team1540/rooster/testers/package-info.java @@ -0,0 +1,4 @@ +/** + * Various classes for testing common things. + */ +package org.team1540.rooster.testers; From 3a7f2fe96c7cd9d733ac4c1f8cbd0b7a2265f158 Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Sun, 6 Jan 2019 14:31:36 -0800 Subject: [PATCH 21/29] Move testers to new directory structure --- .../main/java/org/team1540/rooster/testers/AbstractTester.java | 0 .../java/org/team1540/rooster/testers/ResultWithMetadata.java | 0 .../main/java/org/team1540/rooster/testers/Tester.java | 0 .../java/org/team1540/rooster/testers/motor/BurnoutTester.java | 0 .../team1540/rooster/testers/motor/ControllersMultiTester.java | 0 .../java/org/team1540/rooster/testers/motor/EncoderTester.java | 0 .../team1540/rooster/testers/motor/SimpleControllersTester.java | 0 .../main/java/org/team1540/rooster/testers/package-info.java | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename {lib/src => src}/main/java/org/team1540/rooster/testers/AbstractTester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/ResultWithMetadata.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/Tester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/motor/EncoderTester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java (100%) rename {lib/src => src}/main/java/org/team1540/rooster/testers/package-info.java (100%) diff --git a/lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java b/src/main/java/org/team1540/rooster/testers/AbstractTester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/AbstractTester.java rename to src/main/java/org/team1540/rooster/testers/AbstractTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java b/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java rename to src/main/java/org/team1540/rooster/testers/ResultWithMetadata.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/Tester.java b/src/main/java/org/team1540/rooster/testers/Tester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/Tester.java rename to src/main/java/org/team1540/rooster/testers/Tester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java b/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java rename to src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java rename to src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java rename to src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java b/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java rename to src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java diff --git a/lib/src/main/java/org/team1540/rooster/testers/package-info.java b/src/main/java/org/team1540/rooster/testers/package-info.java similarity index 100% rename from lib/src/main/java/org/team1540/rooster/testers/package-info.java rename to src/main/java/org/team1540/rooster/testers/package-info.java From f474877c12cab10a26989244b48ba9e92f2d4a8b Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Sun, 6 Jan 2019 20:04:01 -0800 Subject: [PATCH 22/29] Update tester for 2019 (mostly) Move testers to new directory structure Java 11 cleanup Warning cleanup Migrate BurnoutTester to use TalonSRXs. Note this breaks SimpleControllersTester, this will need to be fixed before merging. --- .../rooster/testers/motor/BurnoutTester.java | 38 ++++++++----------- .../testers/motor/ControllersMultiTester.java | 11 +++++- .../rooster/testers/motor/EncoderTester.java | 28 +++++++------- .../motor/SimpleControllersTester.java | 8 ++-- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java b/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java index 9ae39f9d..cf18447c 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -1,6 +1,6 @@ package org.team1540.rooster.testers.motor; -import com.ctre.phoenix.motorcontrol.IMotorController; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -16,7 +16,7 @@ * motors and reporting low outliers or checking if a single motor is below the cutoff. */ @SuppressWarnings("unused") -public class BurnoutTester extends AbstractTester implements Sendable { +public class BurnoutTester extends AbstractTester implements Sendable { private static final Median medianCalculator = new Median(); private static final StandardDeviation stdDevCalculator = new StandardDeviation(); @@ -30,28 +30,27 @@ public class BurnoutTester extends AbstractTester imp /** * 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 is more - * than one motor and {@link BurnoutTester#testBurnoutSingleMotor(IMotorController)} if there is + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(TalonSRX)} if there is more + * than one motor and {@link BurnoutTester#testBurnoutSingleMotor(TalonSRX)} 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. */ - @SuppressWarnings("WeakerAccess") - public BurnoutTester(IMotorController... motorsToTest) { + public BurnoutTester(TalonSRX... motorsToTest) { this(Arrays.asList(motorsToTest)); } /** * 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 + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(TalonSRX)} if there are more + * than two motor sand {@link BurnoutTester#testBurnoutSingleMotor(TalonSRX)} if there two * two or few motors. * * @param motorsToTest The motors to compare to each other. */ @SuppressWarnings("WeakerAccess") - 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, null, 150, 500); this.setTest(motorsToTest.size() > 2 ? this::testBurnoutMultiMotor : @@ -65,7 +64,7 @@ public BurnoutTester(List motorsToTest) { * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public boolean testBurnoutMultiMotor(IMotorController controller) { + public boolean testBurnoutMultiMotor(TalonSRX controller) { return controller.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); } @@ -77,7 +76,7 @@ public boolean testBurnoutMultiMotor(IMotorController controller) { * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public boolean testBurnoutSingleMotor(IMotorController controller) { + public boolean testBurnoutSingleMotor(TalonSRX controller) { return controller.getMotorOutputPercent() > percentOutputCutoff && controller.getOutputCurrent() < currentCutoff; } @@ -87,7 +86,7 @@ public boolean testBurnoutSingleMotor(IMotorController controller) { */ @Override protected void periodic() { - double[] currents = getItemsToTest().stream().mapToDouble(IMotorController::getOutputCurrent) + double[] currents = getItemsToTest().stream().mapToDouble(TalonSRX::getOutputCurrent) .toArray(); medianCurrent = medianCalculator.evaluate(currents); stdDevCurrent = stdDevCalculator.evaluate(currents); @@ -156,18 +155,11 @@ public void setPercentOutputCutoff(double percentOutputCutoff) { */ @Override public void initSendable(SendableBuilder builder) { - //noinspection Duplicates - for (IMotorController t : getItemsToTest()) { + for (TalonSRX 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 - Optional> result = Optional.ofNullable(peekMostRecentResult(t)); - if (result.isPresent()) { - return result.get().getResult(); - } else { - return false; - } - }, null); + builder.addBooleanProperty(t.getDeviceID() + "", + () -> Optional.ofNullable(peekMostRecentResult(t)) + .map(ResultWithMetadata::getResult).orElse(false), null); } builder.addDoubleProperty("Median current", () -> medianCurrent, null); } diff --git a/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java index 14f12d0e..5ad569b4 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -3,14 +3,17 @@ import com.ctre.phoenix.motorcontrol.ControlMode; import com.ctre.phoenix.motorcontrol.IMotorController; import com.ctre.phoenix.motorcontrol.NeutralMode; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; 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.Arrays; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.team1540.rooster.testers.AbstractTester; import org.team1540.rooster.testers.ResultWithMetadata; import org.team1540.rooster.wrappers.ChickenTalon; @@ -19,6 +22,7 @@ * Simple automated testing for motors. Add the motors with the specified test using the builder * methods, then run the command. */ +@SuppressWarnings("unused") public class ControllersMultiTester extends Command { private Timer timer = new Timer(); @@ -66,6 +70,7 @@ public ControllersMultiTester() { * @param controllerGroup The motors to add. * @return this */ + @SuppressWarnings("WeakerAccess") public ControllersMultiTester addControllerGroup(IMotorController... controllerGroup) { addControllerGroup(this::setMotorToFull, controllerGroup); return this; @@ -81,7 +86,10 @@ public ControllersMultiTester addControllerGroup(IMotorController... controllerG @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) public ControllersMultiTester addControllerGroup(Consumer function, IMotorController... controllerGroup) { - tests.add(new TesterAndCommand(new BurnoutTester(controllerGroup), function)); + List talons = + Arrays.stream(controllerGroup).filter(TalonSRX.class::isInstance).map(TalonSRX.class::cast) + .collect(Collectors.toList()); + tests.add(new TesterAndCommand(new BurnoutTester(talons), function)); return this; } @@ -91,6 +99,7 @@ public ControllersMultiTester addControllerGroup(Consumer func * @param controllerGroup The motors to add. * @return this */ + @SuppressWarnings("WeakerAccess") public ControllersMultiTester addEncoderGroup(ChickenTalon... controllerGroup) { addEncoderGroup(this::setMotorToFull, controllerGroup); return this; diff --git a/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index f3dcc8e0..5b85a805 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -1,6 +1,7 @@ package org.team1540.rooster.testers.motor; import com.ctre.phoenix.motorcontrol.IMotorController; +import com.ctre.phoenix.motorcontrol.can.TalonSRX; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -42,11 +43,17 @@ public EncoderTester(IMotorController... motorsToTest) { @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. + // Test will run if the motor is drawing over 1A of current if telemetry is available, or else, super((stupid) -> null, motorsToTest, null, 150, 500); this.setTest(this::testEncoder); this.setRunConditions( - Collections.singletonList((motor) -> (motor).getOutputCurrent() > currentThreshold)); + Collections.singletonList((motor) -> { + if (motor instanceof TalonSRX) { + return ((TalonSRX) motor).getOutputCurrent() > currentThreshold; + } else { + return true; + } + })); } /** @@ -60,7 +67,8 @@ public EncoderTester(List motorsToTest) { @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 + return (controller instanceof TalonSRX + && ((TalonSRX) controller).getOutputCurrent() > currentThreshold) && Math.abs(controller.getSelectedSensorVelocity(0)) < velocityThreshold; } @@ -127,19 +135,11 @@ 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 - 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); + builder.addBooleanProperty(t.getDeviceID() + "", + () -> Optional.ofNullable(peekMostRecentResult(t)) + .map(ResultWithMetadata::getResult).orElse(false), null); } } } diff --git a/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java b/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java index b5d6cfdb..967287c5 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/SimpleControllersTester.java @@ -22,6 +22,7 @@ * the motor in the SmartDashboard or move next/previous with buttons and control the percent * output of the motor with a joystick axis. */ +@SuppressWarnings("unused") public class SimpleControllersTester extends Command implements Sendable { private static final int DEFAULT_JOYSTICK_ID = 0; @@ -49,6 +50,7 @@ public class SimpleControllersTester extends Command implements Sendable { * * @param controllers The {@link IMotorController IMotorControllers} to test. */ + @SuppressWarnings({"SpellCheckingInspection", "unused"}) public SimpleControllersTester(IMotorController... controllers) { this(new Joystick(DEFAULT_JOYSTICK_ID), DEFAULT_AXIS_ID, DEFAULT_NEXT_BUTTON_ID, DEFAULT_PREVIOUS_BUTTON_ID, controllers); @@ -83,10 +85,10 @@ public SimpleControllersTester(Joystick joystick, int axisId, int nextButtonId, // Add a chooser that you can use to select the controller and initialize it to null, // corresponding with buttons should be used - this.controllerChooser.addObject("Use buttons", null); + this.controllerChooser.addOption("Use buttons", null); for (IMotorController controller : controllers) { this.controllers.put(controller.getDeviceID(), controller); - this.controllerChooser.addObject(String.valueOf(controller.getDeviceID()), + this.controllerChooser.addOption(String.valueOf(controller.getDeviceID()), controller.getDeviceID()); controller.setNeutralMode(NeutralMode.Coast); } @@ -152,7 +154,7 @@ public void initSendable(SendableBuilder builder) { * active {@link IMotorController} and information about the tester. Follows a builder pattern. * @return This {@code SimpleControllersTester} in a builder pattern. */ - @SuppressWarnings("UnusedReturnValue") + @SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection"}) @Contract(" -> this") public SimpleControllersTester addAllSendables() { SmartDashboard.putData("Controller tester info", this); From 81005f3238f5aba852a35484cbe882eddb4cab9e Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 7 Jan 2019 15:29:36 -0800 Subject: [PATCH 23/29] Migrate all testers to ChickenTalon RIP Victors. However, for our use case, we probably don't actually need them. --- .../rooster/testers/motor/BurnoutTester.java | 25 ++++++------ .../testers/motor/ControllersMultiTester.java | 39 ++++++++----------- .../rooster/testers/motor/EncoderTester.java | 34 +++++++--------- 3 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java b/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java index cf18447c..19c24b61 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/BurnoutTester.java @@ -1,6 +1,5 @@ package org.team1540.rooster.testers.motor; -import com.ctre.phoenix.motorcontrol.can.TalonSRX; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -10,13 +9,14 @@ import org.apache.commons.math3.stat.descriptive.rank.Median; import org.team1540.rooster.testers.AbstractTester; import org.team1540.rooster.testers.ResultWithMetadata; +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 or checking if a single motor is below the cutoff. */ @SuppressWarnings("unused") -public class BurnoutTester extends AbstractTester implements Sendable { +public class BurnoutTester extends AbstractTester implements Sendable { private static final Median medianCalculator = new Median(); private static final StandardDeviation stdDevCalculator = new StandardDeviation(); @@ -30,27 +30,28 @@ public class BurnoutTester extends AbstractTester implements /** * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 - * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(TalonSRX)} if there is more - * than one motor and {@link BurnoutTester#testBurnoutSingleMotor(TalonSRX)} if there is + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(ChickenTalon)} if there is more + * than one motor and {@link BurnoutTester#testBurnoutSingleMotor(ChickenTalon)} 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. */ - public BurnoutTester(TalonSRX... motorsToTest) { + @SuppressWarnings("WeakerAccess") + public BurnoutTester(ChickenTalon... motorsToTest) { this(Arrays.asList(motorsToTest)); } /** * Construct a new instance with the default logTime of 150 seconds and an update delay of 500 - * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(TalonSRX)} if there are more - * than two motor sand {@link BurnoutTester#testBurnoutSingleMotor(TalonSRX)} if there two + * ms, using the {@link BurnoutTester#testBurnoutMultiMotor(ChickenTalon)} if there are more + * than two motor sand {@link BurnoutTester#testBurnoutSingleMotor(ChickenTalon)} if there two * two or few motors. * * @param motorsToTest The motors to compare to each other. */ @SuppressWarnings("WeakerAccess") - 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, null, 150, 500); this.setTest(motorsToTest.size() > 2 ? this::testBurnoutMultiMotor : @@ -64,7 +65,7 @@ public BurnoutTester(List motorsToTest) { * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public boolean testBurnoutMultiMotor(TalonSRX controller) { + public boolean testBurnoutMultiMotor(ChickenTalon controller) { return controller.getOutputCurrent() < (this.medianCurrent - 1 * this.stdDevCurrent); } @@ -76,7 +77,7 @@ public boolean testBurnoutMultiMotor(TalonSRX controller) { * @return Boolean indicating burnout. */ @SuppressWarnings("WeakerAccess") - public boolean testBurnoutSingleMotor(TalonSRX controller) { + public boolean testBurnoutSingleMotor(ChickenTalon controller) { return controller.getMotorOutputPercent() > percentOutputCutoff && controller.getOutputCurrent() < currentCutoff; } @@ -86,7 +87,7 @@ public boolean testBurnoutSingleMotor(TalonSRX controller) { */ @Override protected void periodic() { - double[] currents = getItemsToTest().stream().mapToDouble(TalonSRX::getOutputCurrent) + double[] currents = getItemsToTest().stream().mapToDouble(ChickenTalon::getOutputCurrent) .toArray(); medianCurrent = medianCalculator.evaluate(currents); stdDevCurrent = stdDevCalculator.evaluate(currents); @@ -155,7 +156,7 @@ public void setPercentOutputCutoff(double percentOutputCutoff) { */ @Override public void initSendable(SendableBuilder builder) { - for (TalonSRX t : getItemsToTest()) { + for (ChickenTalon t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder builder.addBooleanProperty(t.getDeviceID() + "", () -> Optional.ofNullable(peekMostRecentResult(t)) diff --git a/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java b/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java index 5ad569b4..3214968b 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/ControllersMultiTester.java @@ -1,19 +1,15 @@ package org.team1540.rooster.testers.motor; import com.ctre.phoenix.motorcontrol.ControlMode; -import com.ctre.phoenix.motorcontrol.IMotorController; import com.ctre.phoenix.motorcontrol.NeutralMode; -import com.ctre.phoenix.motorcontrol.can.TalonSRX; 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.Arrays; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; import org.team1540.rooster.testers.AbstractTester; import org.team1540.rooster.testers.ResultWithMetadata; import org.team1540.rooster.wrappers.ChickenTalon; @@ -32,7 +28,7 @@ public class ControllersMultiTester extends Command { /** * Default constructor. Note that this class follows a builder pattern; use - * {@link #addControllerGroup(IMotorController...)} and + * {@link #addControllerGroup(ChickenTalon...)} and * {@link #addEncoderGroup(ChickenTalon...)} to add the motors. */ public ControllersMultiTester() { @@ -42,7 +38,7 @@ public ControllersMultiTester() { stateMachineConfig.configure(State.SPIN_UP) .permit(Trigger.TIME_HAS_PASSED, State.EXECUTING) .onEntry(() -> { - for (IMotorController motor : tests.get(index).getTest().getItemsToTest()) { + for (ChickenTalon motor : tests.get(index).getTest().getItemsToTest()) { tests.get(index).getFunction().accept(motor); } }); @@ -54,7 +50,7 @@ public ControllersMultiTester() { .permit(Trigger.TIME_HAS_PASSED, State.SPIN_UP) .permit(Trigger.FINISHED, State.FINISHED) .onEntry(() -> { - for (IMotorController motor : tests.get(index).getTest().getItemsToTest()) { + for (ChickenTalon motor : tests.get(index).getTest().getItemsToTest()) { motor.set(ControlMode.PercentOutput, 0); } index++; @@ -71,7 +67,7 @@ public ControllersMultiTester() { * @return this */ @SuppressWarnings("WeakerAccess") - public ControllersMultiTester addControllerGroup(IMotorController... controllerGroup) { + public ControllersMultiTester addControllerGroup(ChickenTalon... controllerGroup) { addControllerGroup(this::setMotorToFull, controllerGroup); return this; } @@ -84,12 +80,9 @@ public ControllersMultiTester addControllerGroup(IMotorController... controllerG * @return this */ @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) - public ControllersMultiTester addControllerGroup(Consumer function, - IMotorController... controllerGroup) { - List talons = - Arrays.stream(controllerGroup).filter(TalonSRX.class::isInstance).map(TalonSRX.class::cast) - .collect(Collectors.toList()); - tests.add(new TesterAndCommand(new BurnoutTester(talons), function)); + public ControllersMultiTester addControllerGroup(Consumer function, + ChickenTalon... controllerGroup) { + tests.add(new TesterAndCommand(new BurnoutTester(controllerGroup), function)); return this; } @@ -113,7 +106,7 @@ public ControllersMultiTester addEncoderGroup(ChickenTalon... controllerGroup) { * @return this */ @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) - public ControllersMultiTester addEncoderGroup(Consumer function, + public ControllersMultiTester addEncoderGroup(Consumer function, ChickenTalon... controllerGroup) { tests.add(new TesterAndCommand(new EncoderTester(controllerGroup), function)); return this; @@ -142,7 +135,7 @@ protected void execute() { protected void end() { // Very optimized yes no duplicate code either for (TesterAndCommand testerAndCommand : tests) { - for (IMotorController controller : testerAndCommand.getTest().getItemsToTest()) { + for (ChickenTalon controller : testerAndCommand.getTest().getItemsToTest()) { int failureCount = 0; for (ResultWithMetadata result : testerAndCommand.getTest() .getStoredResults(controller)) { @@ -165,7 +158,7 @@ protected boolean isFinished() { return stateMachine.getState().equals(State.FINISHED); } - private void setMotorToFull(IMotorController motor) { + private void setMotorToFull(ChickenTalon motor) { motor.setNeutralMode(NeutralMode.Coast); motor.set(ControlMode.PercentOutput, 1.0); } @@ -194,21 +187,21 @@ private enum Trigger { private class TesterAndCommand { - private AbstractTester test; - private Consumer function; + private AbstractTester test; + private Consumer function; private TesterAndCommand( - AbstractTester test, - Consumer function) { + AbstractTester test, + Consumer function) { this.test = test; this.function = function; } - private AbstractTester getTest() { + private AbstractTester getTest() { return test; } - private Consumer getFunction() { + private Consumer getFunction() { return function; } } diff --git a/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java b/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java index 5b85a805..4b4d9805 100644 --- a/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java +++ b/src/main/java/org/team1540/rooster/testers/motor/EncoderTester.java @@ -1,7 +1,5 @@ package org.team1540.rooster.testers.motor; -import com.ctre.phoenix.motorcontrol.IMotorController; -import com.ctre.phoenix.motorcontrol.can.TalonSRX; import edu.wpi.first.wpilibj.Sendable; import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; import java.util.Arrays; @@ -10,13 +8,14 @@ import java.util.Optional; import org.team1540.rooster.testers.AbstractTester; import org.team1540.rooster.testers.ResultWithMetadata; +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 { +public class EncoderTester extends AbstractTester implements Sendable { private String name = "EncoderTester"; @@ -28,47 +27,40 @@ public class EncoderTester extends AbstractTester imp * ms. Equivalent to {@link EncoderTester#EncoderTester(List) EncoderTester(Arrays.asList * (motorsToTest))}. * - * @param motorsToTest The {@link IMotorController IMotorControllers} to compare to each other. + * @param motorsToTest The {@link ChickenTalon ChickenTalons} to compare to each other. */ @SuppressWarnings("WeakerAccess") - public EncoderTester(IMotorController... motorsToTest) { + 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 IMotorController IMotorControllers} to compare to each other. + * @param motorsToTest The {@link ChickenTalon ChickenTalons} 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 if telemetry is available, or else, super((stupid) -> null, motorsToTest, null, 150, 500); this.setTest(this::testEncoder); this.setRunConditions( - Collections.singletonList((motor) -> { - if (motor instanceof TalonSRX) { - return ((TalonSRX) motor).getOutputCurrent() > currentThreshold; - } else { - return true; - } - })); + 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 IMotorController} is moving at a velocity of less than 5. + * than 1 amp and if the selected {@link ChickenTalon} is moving at a velocity of less than 5. * - * @param controller The {@link IMotorController} to test for burnout. + * @param controller The {@link ChickenTalon} to test for burnout. * @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) { + public Boolean testEncoder(ChickenTalon controller) { // Do the wrappers provide pidIdx nicely? Yes. Can we just use zero? Also probably yes. - return (controller instanceof TalonSRX - && ((TalonSRX) controller).getOutputCurrent() > currentThreshold) + return controller.getOutputCurrent() > currentThreshold && Math.abs(controller.getSelectedSensorVelocity(0)) < velocityThreshold; } @@ -129,13 +121,13 @@ public void setVelocityThreshold(double velocityThreshold) { } /** - * Displays the current status of each {@link IMotorController}. + * Displays the current status of each {@link ChickenTalon}. * * @param builder The {@link SendableBuilder} to use. */ @Override public void initSendable(SendableBuilder builder) { - for (IMotorController t : getItemsToTest()) { + for (ChickenTalon t : getItemsToTest()) { // Get the most recent value if present, else simply don't add it to the builder builder.addBooleanProperty(t.getDeviceID() + "", () -> Optional.ofNullable(peekMostRecentResult(t)) From 0283c55056ced21a308da4286df6ad1054da6ebd Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 7 Jan 2019 16:03:55 -0800 Subject: [PATCH 24/29] Fix ChickenController Correct inheritance, update annotations and suppression to match. --- .../rooster/wrappers/ChickenController.java | 168 +----------------- .../rooster/wrappers/ChickenTalon.java | 30 +--- .../rooster/wrappers/ChickenVictor.java | 70 +++++++- 3 files changed, 71 insertions(+), 197 deletions(-) diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java index 06aa9b91..a650b7c6 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java @@ -14,6 +14,7 @@ import com.ctre.phoenix.motorcontrol.StatusFrame; import com.ctre.phoenix.motorcontrol.StatusFrameEnhanced; +@SuppressWarnings({"unused", "UnusedReturnValue"}) public interface ChickenController extends IMotorController { ErrorCode clearMotionProfileHasUnderrun(); @@ -355,32 +356,6 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ ErrorCode config_kP(int slotIdx, double value); - /** - * Get the position of whatever is in the analog pin of the Talon, - * regardless of whether it is actually being used for feedback. - * - * @return the 24bit analog value. The bottom ten bits is the ADC (0 - 1023) - * on the analog pin of the Talon. The upper 14 bits tracks the - * overflows and underflows (continuous sensor). - */ - int getAnalogIn(); - - /** - * Get the position of whatever is in the analog pin of the Talon, - * regardless of whether it is actually being used for feedback. - * - * @return the ADC (0 - 1023) on analog pin of the Talon. - */ - int getAnalogInRaw(); - - /** - * Get the position of whatever is in the analog pin of the Talon, - * regardless of whether it is actually being used for feedback. - * - * @return the value (0 - 1023) on the analog pin of the Talon. - */ - int getAnalogInVel(); - /** * Gets the closed-loop error. * @@ -438,85 +413,6 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ double getIntegralAccumulator(); - /** - * Gets pin state quad a. - * - * @return the pin state quad a. - */ - boolean getPinStateQuadA(); - - /** - * Gets pin state quad b. - * - * @return Digital level of QUADB pin. - */ - boolean getPinStateQuadB(); - - /** - * Gets pin state quad index. - * - * @return Digital level of QUAD Index pin. - */ - boolean getPinStateQuadIdx(); - - /** - * Gets pulse width position. - * - * @return the pulse width position. - */ - int getPulseWidthPosition(); - - /** - * Gets pulse width rise to fall us. - * - * @return the pulse width rise to fall us. - */ - int getPulseWidthRiseToFallUs(); - - /** - * Gets pulse width rise to rise us. - * - * @return the pulse width rise to rise us. - */ - int getPulseWidthRiseToRiseUs(); - - /** - * Gets pulse width velocity. - * - * @return the pulse width velocity. - */ - int getPulseWidthVelocity(); - - /** - * Get the position of whatever is in the analog pin of the Talon, - * regardless of whether it is actually being used for feedback. - * - * @return the Error code of the request. - */ - int getQuadraturePosition(); - - /** - * Get the position of whatever is in the analog pin of the Talon, - * regardless of whether it is actually being used for feedback. - * - * @return the value (0 - 1023) on the analog pin of the Talon. - */ - int getQuadratureVelocity(); - - /** - * Get the selected sensor position. - * - * @return Position of selected sensor in raw sensor units per decisecond. - */ - int getSelectedSensorPosition(); - - /** - * Get the selected sensor velocity. - * - * @return Velocity of selected sensor in raw sensor units per decisecond. - */ - int getSelectedSensorVelocity(); - int getStatusFramePeriod(StatusFrameEnhanced frame); /** @@ -535,24 +431,6 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ int getStatusFramePeriod(StatusFrame frame); - /** - * Is forward limit switch closed. - * - * @return '1' iff forward limit switch is closed, 0 iff switch is open. - * This function works regardless if limit switch feature is - * enabled. - */ - boolean isFwdLimitSwitchClosed(); - - /** - * Is reverse limit switch closed. - * - * @return '1' iff reverse limit switch is closed, 0 iff switch is open. - * This function works regardless if limit switch feature is - * enabled. - */ - boolean isRevLimitSwitchClosed(); - /** * Selects which profile slot to use for closed-loop control. * @@ -574,14 +452,6 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ void set(double outputValue); - /** - * Sets analog position. - * - * @param newPosition The new position. - * @return an ErrorCode. - */ - ErrorCode setAnalogPosition(int newPosition); - /** * Sets the mode of operation during neutral throttle output. * @@ -606,42 +476,6 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ ErrorCode setIntegralAccumulator(double iaccum); - /** - * Sets pulse width position. - * - * @param newPosition The position value to apply to the sensor. - * @return an ErrErrorCode - */ - ErrorCode setPulseWidthPosition(int newPosition); - - /** - * Change the quadrature reported position. Typically this is used to "zero" - * the sensor. This only works with Quadrature sensor. To set the selected - * sensor position regardless of what type it is, see - * SetSelectedSensorPosition in the motor controller class. - * - * @param newPosition The position value to apply to the sensor. - * @return error code. - */ - ErrorCode setQuadraturePosition(int newPosition); - - /** - * Sets the sensor position to the given value. - * - * @param sensorPos Position to set for the selected sensor (in Raw Sensor Units). - * @param pidIdx The PID IDX to use. - * @return Error Code generated by function. 0 indicates no error. - */ - ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx); - - /** - * Sets the sensor position to the given value. - * - * @param sensorPos Position to set for the selected sensor (in Raw Sensor Units). - * @return Error Code generated by function. 0 indicates no error. - */ - ErrorCode setSelectedSensorPosition(int sensorPos); - /** * Sets the period of the given status frame. * diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java b/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java index b46a851a..dbca19fc 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenTalon.java @@ -20,6 +20,7 @@ * Wrapper around a {@link TalonSRX} adding some features (that really should already be there) as * well as making functions easier to call. */ +@SuppressWarnings({"unused", "WeakerAccess"}) public class ChickenTalon extends TalonSRX implements ChickenController { ControlMode controlMode = ControlMode.PercentOutput; @@ -507,7 +508,6 @@ public ErrorCode config_kP(int slotIdx, double value) { * @return the 24bit analog value. The bottom ten bits is the ADC (0 - 1023) on the analog pin of * the Talon. The upper 14 bits tracks the overflows and underflows (continuous sensor). */ - @Override public int getAnalogIn() { return getSensorCollection().getAnalogIn(); } @@ -518,7 +518,6 @@ public int getAnalogIn() { * * @return the ADC (0 - 1023) on analog pin of the Talon. */ - @Override public int getAnalogInRaw() { return getSensorCollection().getAnalogInRaw(); } @@ -529,7 +528,6 @@ public int getAnalogInRaw() { * * @return the value (0 - 1023) on the analog pin of the Talon. */ - @Override public int getAnalogInVel() { return getSensorCollection().getAnalogInVel(); } @@ -628,7 +626,6 @@ public double getIntegralAccumulator() { * * @return the pin state quad a. */ - @Override public boolean getPinStateQuadA() { return getSensorCollection().getPinStateQuadA(); } @@ -638,7 +635,6 @@ public boolean getPinStateQuadA() { * * @return Digital level of QUADB pin. */ - @Override public boolean getPinStateQuadB() { return getSensorCollection().getPinStateQuadB(); } @@ -648,7 +644,6 @@ public boolean getPinStateQuadB() { * * @return Digital level of QUAD Index pin. */ - @Override public boolean getPinStateQuadIdx() { return getSensorCollection().getPinStateQuadIdx(); } @@ -658,7 +653,6 @@ public boolean getPinStateQuadIdx() { * * @return the pulse width position. */ - @Override public int getPulseWidthPosition() { return getSensorCollection().getPulseWidthPosition(); } @@ -668,7 +662,6 @@ public int getPulseWidthPosition() { * * @return the pulse width rise to fall us. */ - @Override public int getPulseWidthRiseToFallUs() { return getSensorCollection().getPulseWidthRiseToFallUs(); } @@ -678,7 +671,6 @@ public int getPulseWidthRiseToFallUs() { * * @return the pulse width rise to rise us. */ - @Override public int getPulseWidthRiseToRiseUs() { return getSensorCollection().getPulseWidthRiseToRiseUs(); } @@ -688,7 +680,6 @@ public int getPulseWidthRiseToRiseUs() { * * @return the pulse width velocity. */ - @Override public int getPulseWidthVelocity() { return getSensorCollection().getPulseWidthVelocity(); } @@ -699,7 +690,6 @@ public int getPulseWidthVelocity() { * * @return the Error code of the request. */ - @Override public int getQuadraturePosition() { return getSensorCollection().getQuadraturePosition(); } @@ -710,7 +700,6 @@ public int getQuadraturePosition() { * * @return the value (0 - 1023) on the analog pin of the Talon. */ - @Override public int getQuadratureVelocity() { return getSensorCollection().getQuadratureVelocity(); } @@ -768,7 +757,6 @@ public int getStatusFramePeriod(StatusFrame frame) { * @return '1' iff forward limit switch is closed, 0 iff switch is open. This function works * regardless if limit switch feature is enabled. */ - @Override public boolean isFwdLimitSwitchClosed() { return getSensorCollection().isFwdLimitSwitchClosed(); } @@ -779,7 +767,6 @@ public boolean isFwdLimitSwitchClosed() { * @return '1' iff reverse limit switch is closed, 0 iff switch is open. This function works * regardless if limit switch feature is enabled. */ - @Override public boolean isRevLimitSwitchClosed() { return getSensorCollection().isRevLimitSwitchClosed(); } @@ -815,7 +802,6 @@ public void set(double outputValue) { * @param newPosition The new position. * @return an ErrorCode. */ - @Override public ErrorCode setAnalogPosition(int newPosition) { return getSensorCollection().setAnalogPosition(newPosition, defaultTimeoutMs); } @@ -859,7 +845,6 @@ public ErrorCode setIntegralAccumulator(double iaccum) { * @param newPosition The position value to apply to the sensor. * @return an ErrErrorCode */ - @Override public ErrorCode setPulseWidthPosition(int newPosition) { return getSensorCollection().setPulseWidthPosition(newPosition, defaultTimeoutMs); } @@ -872,7 +857,6 @@ public ErrorCode setPulseWidthPosition(int newPosition) { * @param newPosition The position value to apply to the sensor. * @return error code. */ - @Override public ErrorCode setQuadraturePosition(int newPosition) { return getSensorCollection().setQuadraturePosition(newPosition, defaultTimeoutMs); } @@ -884,7 +868,6 @@ public ErrorCode setQuadraturePosition(int newPosition) { * @param pidIdx The PID IDX to use. * @return Error Code generated by function. 0 indicates no error. */ - @Override public ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx) { return super.setSelectedSensorPosition(sensorPos, pidIdx, defaultTimeoutMs); } @@ -942,7 +925,6 @@ public void setControlMode(int mode) { /** * @deprecated Use {@link #setControlMode(ControlMode)} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void changeControlMode(TalonControlMode controlMode) { if (controlMode.ctrl != null) { @@ -1019,7 +1001,6 @@ public void enableBrakeMode(boolean brake) { * @param codesPerRev The number of counts per revolution. * @deprecated Functionality removed. */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void configEncoderCodesPerRev(int codesPerRev) { this.setEncoderCodesPerRev(codesPerRev); @@ -1049,7 +1030,6 @@ public void setPosition(double pos) { /** * @deprecated Use {@link #configSelectedFeedbackSensor(FeedbackDevice)} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void setFeedbackDevice(FeedbackDevice device) { configSelectedFeedbackSensor(device); @@ -1092,7 +1072,6 @@ public int getEncPosition() { * * @deprecated Use {@link #getQuadratureVelocity()} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public int getEncVelocity() { return getQuadratureVelocity(); @@ -1116,7 +1095,6 @@ public double getError() { * @param flip True if motor output should be flipped; False if not. * @deprecated Use {@link #setInverted(boolean)} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void reverseOutput(boolean flip) { setInverted(flip); @@ -1130,7 +1108,6 @@ public void reverseOutput(boolean flip) { * @param flip True if sensor input should be flipped; False if not. * @deprecated Use {@link #setSensorPhase(boolean)} */ - @SuppressWarnings("DeprecatedIsStillUsed") public void reverseSensor(boolean flip) { setSensorPhase(flip); } @@ -1139,7 +1116,6 @@ public void reverseSensor(boolean flip) { * @deprecated Use {@link #configNominalOutputForward(double)} and * {@link #configNominalOutputReverse(double)} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void configNominalOutputVoltage(double forwardVoltage, double reverseVoltage) { configNominalOutputForward(forwardVoltage / 12); @@ -1169,7 +1145,6 @@ public void setSetpoint(double setpoint) { * @deprecated Use {@link #configPeakOutputForward(double) and * {@link #configPeakOutputReverse(double)}} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public void configPeakOutputVoltage(double forwardVoltage, double reverseVoltage) { configPeakOutputForward(forwardVoltage / 12); @@ -1180,7 +1155,6 @@ public void configPeakOutputVoltage(double forwardVoltage, double reverseVoltage * @return The voltage being output by the Talon, in Volts. * @deprecated Use {@link #getMotorOutputVoltage()} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public double getOutputVoltage() { return getMotorOutputVoltage(); @@ -1197,7 +1171,6 @@ public double getOutputVoltage() { * @return The speed of the sensor currently providing feedback. * @deprecated Use {@link #getSelectedSensorVelocity()} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public double getSpeed() { return getSelectedSensorVelocity(); @@ -1206,7 +1179,6 @@ public double getSpeed() { /** * @deprecated Use {@link ControlMode} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated public enum TalonControlMode { PercentVbus(0, ControlMode.PercentOutput), diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java index 335d8b8c..40c9f1bc 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java @@ -16,7 +16,8 @@ import com.ctre.phoenix.motorcontrol.can.MotControllerJNI; import com.ctre.phoenix.motorcontrol.can.VictorSPX; -public class ChickenVictor extends VictorSPX { +@SuppressWarnings("unused") +public class ChickenVictor extends VictorSPX implements ChickenController { ControlMode controlMode = ControlMode.PercentOutput; int defaultPidIdx = 0; @@ -28,271 +29,335 @@ public ChickenVictor(int deviceNumber) { super(deviceNumber); } + @Override public ErrorCode clearMotionProfileHasUnderrun() { return super.clearMotionProfileHasUnderrun(defaultTimeoutMs); } + @Override public ErrorCode clearStickyFaults() { return super.clearStickyFaults(defaultTimeoutMs); } + @Override public ErrorCode configAllowableClosedloopError(int slotIdx, int allowableClosedLoopError) { return super .configAllowableClosedloopError(slotIdx, allowableClosedLoopError, defaultTimeoutMs); } + @Override public ErrorCode configClosedloopRamp(double secondsFromNeutralToFull) { return super.configClosedloopRamp(secondsFromNeutralToFull, defaultTimeoutMs); } + @Override public ErrorCode configOpenloopRamp(double secondsFromNeutralToFull) { return super.configOpenloopRamp(secondsFromNeutralToFull, defaultTimeoutMs); } + @Override public ErrorCode configForwardLimitSwitchSource(LimitSwitchSource type, LimitSwitchNormal normalOpenOrClose) { return super.configForwardLimitSwitchSource(type, normalOpenOrClose, defaultTimeoutMs); } + @Override public ErrorCode configForwardLimitSwitchSource(RemoteLimitSwitchSource type, LimitSwitchNormal normalOpenOrClose, int deviceID) { return super .configForwardLimitSwitchSource(type, normalOpenOrClose, deviceID, defaultTimeoutMs); } + @Override public ErrorCode configForwardSoftLimitEnable(boolean enable) { return super.configForwardSoftLimitEnable(enable, defaultTimeoutMs); } + @Override public ErrorCode configForwardSoftLimitThreshold(int forwardSensorLimit) { return super.configForwardSoftLimitThreshold(forwardSensorLimit, defaultTimeoutMs); } + @Override public int configGetCustomParam(int paramIndex) { return super.configGetCustomParam(paramIndex, defaultTimeoutMs); } + @Override public double configGetParameter(ParamEnum param, int ordinal) { return super.configGetParameter(param, ordinal, defaultTimeoutMs); } + @Override public double configGetParameter(int param, int ordinal) { return super.configGetParameter(param, ordinal, defaultTimeoutMs); } + @Override public ErrorCode configMaxIntegralAccumulator(int slotIdx, double iaccum) { return super.configMaxIntegralAccumulator(slotIdx, iaccum, defaultTimeoutMs); } + @Override public ErrorCode configMotionAcceleration(int sensorUnitsPer100msPerSec) { return super.configMotionAcceleration(sensorUnitsPer100msPerSec, defaultTimeoutMs); } + @Override public ErrorCode configMotionCruiseVelocity(int sensorUnitsPer100ms) { return super.configMotionCruiseVelocity(sensorUnitsPer100ms, defaultTimeoutMs); } + @Override public ErrorCode configNeutralDeadband(double percentDeadband) { return super.configNeutralDeadband(percentDeadband, defaultTimeoutMs); } + @Override public ErrorCode configNominalOutputForward(double percentOut) { return super.configNominalOutputForward(percentOut, defaultTimeoutMs); } + @Override public ErrorCode configNominalOutputReverse(double percentOut) { return super.configNominalOutputReverse(percentOut, defaultTimeoutMs); } + @Override public ErrorCode configPeakOutputForward(double percentOut) { peakOutputForward = percentOut; return super.configPeakOutputForward(percentOut, defaultTimeoutMs); } + @Override public ErrorCode configPeakOutputReverse(double percentOut) { peakOutputReverse = percentOut; return super.configPeakOutputReverse(percentOut, defaultTimeoutMs); } + @Override public double getPeakOutputForward() { return peakOutputForward; } + @Override public double getPeakOutputReverse() { return peakOutputReverse; } + @Override public ErrorCode configRemoteFeedbackFilter(int deviceID, RemoteSensorSource remoteSensorSource, int remoteOrdinal) { return super .configRemoteFeedbackFilter(deviceID, remoteSensorSource, remoteOrdinal, defaultTimeoutMs); } + @Override public ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, LimitSwitchNormal normalOpenOrClose, int deviceID) { return super .configReverseLimitSwitchSource(type, normalOpenOrClose, deviceID, defaultTimeoutMs); } + @Override public ErrorCode configReverseSoftLimitEnable(boolean enable) { return super.configReverseSoftLimitEnable(enable, defaultTimeoutMs); } + @Override public ErrorCode configReverseSoftLimitThreshold(int reverseSensorLimit) { return super.configReverseSoftLimitThreshold(reverseSensorLimit, defaultTimeoutMs); } + @Override public ErrorCode configSelectedFeedbackSensor(RemoteFeedbackDevice feedbackDevice, int pidIdx) { return super.configSelectedFeedbackSensor(feedbackDevice, pidIdx, defaultTimeoutMs); } + @Override public ErrorCode configSelectedFeedbackSensor(RemoteFeedbackDevice feedbackDevice) { return super.configSelectedFeedbackSensor(feedbackDevice, defaultPidIdx, defaultTimeoutMs); } + @Override public ErrorCode configSelectedFeedbackSensor(FeedbackDevice feedbackDevice, int pidIdx) { return super.configSelectedFeedbackSensor(feedbackDevice, pidIdx, defaultTimeoutMs); } + @Override public ErrorCode configSelectedFeedbackSensor(FeedbackDevice feedbackDevice) { return super.configSelectedFeedbackSensor(feedbackDevice, defaultPidIdx, defaultTimeoutMs); } + @Override public ErrorCode configSensorTerm(SensorTerm sensorTerm, FeedbackDevice feedbackDevice) { return super.configSensorTerm(sensorTerm, feedbackDevice, defaultTimeoutMs); } + @Override public ErrorCode configSetCustomParam(int newValue, int paramIndex) { return super.configSetCustomParam(newValue, paramIndex, defaultTimeoutMs); } + @Override public ErrorCode configSetParameter(ParamEnum param, double value, int subValue, int ordinal) { return super.configSetParameter(param, value, subValue, ordinal, defaultTimeoutMs); } + @Override public ErrorCode configSetParameter(int param, double value, int subValue, int ordinal) { return super.configSetParameter(param, value, subValue, ordinal, defaultTimeoutMs); } + @Override public ErrorCode configVelocityMeasurementPeriod(int period) { int retval = MotControllerJNI .ConfigVelocityMeasurementPeriod(m_handle, period, defaultTimeoutMs); return ErrorCode.valueOf(retval); } + @Override public ErrorCode configVelocityMeasurementWindow(int windowSize) { return super.configVelocityMeasurementWindow(windowSize, defaultTimeoutMs); } + @Override public ErrorCode configVoltageCompSaturation(double voltage) { return super.configVoltageCompSaturation(voltage, defaultTimeoutMs); } + @Override public ErrorCode configVoltageMeasurementFilter(int filterWindowSamples) { return super.configVoltageMeasurementFilter(filterWindowSamples, defaultTimeoutMs); } + @Override public ErrorCode config_IntegralZone(int slotIdx, int izone) { return super.config_IntegralZone(slotIdx, izone, defaultTimeoutMs); } + @Override public ErrorCode config_kD(int slotIdx, double value) { return super.config_kD(slotIdx, value, defaultTimeoutMs); } + @Override public ErrorCode config_kF(int slotIdx, double value) { return super.config_kF(slotIdx, value, defaultTimeoutMs); } + @Override public ErrorCode config_kI(int slotIdx, double value) { return super.config_kI(slotIdx, value, defaultTimeoutMs); } + @Override public ErrorCode config_kP(int slotIdx, double value) { return super.config_kP(slotIdx, value, defaultTimeoutMs); } + @Override public int getClosedLoopError() { return super.getClosedLoopError(defaultPidIdx); } + @Override public ControlMode getControlMode() { return controlMode; } + @Override public void setControlMode(ControlMode controlMode) { this.controlMode = controlMode; } + @Override public int getDefaultPidIdx() { return defaultPidIdx; } + @Override public void setDefaultPidIdx(int defaultPidIdx) { this.defaultPidIdx = defaultPidIdx; } + @Override public int getDefaultTimeoutMs() { return defaultTimeoutMs; } + @Override public void setDefaultTimeoutMs(int defaultTimeoutMs) { this.defaultTimeoutMs = defaultTimeoutMs; } + @Override @Deprecated public double getEncoderCodesPerRev() { return 0; } + @Override @Deprecated public void setEncoderCodesPerRev(double encoderCodesPerRev) { } + @Override public double getErrorDerivative() { return super.getErrorDerivative(defaultPidIdx); } + @Override public double getIntegralAccumulator() { return super.getIntegralAccumulator(defaultPidIdx); } + @Override public int getSelectedSensorPosition() { return super.getSelectedSensorPosition(defaultPidIdx); } + @Override public int getSelectedSensorVelocity() { return super.getSelectedSensorVelocity(defaultPidIdx); } + @Override public int getStatusFramePeriod(StatusFrameEnhanced frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } + @Override public int getStatusFramePeriod(int frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } + @Override public int getStatusFramePeriod(StatusFrame frame) { return super.getStatusFramePeriod(frame, defaultTimeoutMs); } + @Override public void selectProfileSlot(int slotIdx) { super.selectProfileSlot(slotIdx, defaultPidIdx); } + @Override public void set(double outputValue) { super.set(controlMode, outputValue); } + @Override public void setBrake(boolean brake) { super.setNeutralMode(brake ? NeutralMode.Brake : NeutralMode.Coast); } + @Override public ErrorCode setIntegralAccumulator(double iaccum, int pidIdx) { return super.setIntegralAccumulator(iaccum, pidIdx, defaultTimeoutMs); } + @Override public ErrorCode setIntegralAccumulator(double iaccum) { return super.setIntegralAccumulator(iaccum, defaultPidIdx, defaultTimeoutMs); } @@ -301,14 +366,17 @@ public ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx) { return super.setSelectedSensorPosition(sensorPos, pidIdx, defaultTimeoutMs); } + @Override public ErrorCode setSelectedSensorPosition(int sensorPos) { return super.setSelectedSensorPosition(sensorPos, defaultPidIdx, defaultTimeoutMs); } + @Override public ErrorCode setStatusFramePeriod(int frameValue, int periodMs) { return super.setStatusFramePeriod(frameValue, periodMs, defaultTimeoutMs); } + @Override public ErrorCode setStatusFramePeriod(StatusFrame frame, int periodMs) { return super.setStatusFramePeriod(frame, periodMs, defaultTimeoutMs); } From 620b270777e212487f26de34afb4ab4d6ea500ce Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Mon, 7 Jan 2019 18:54:34 -0800 Subject: [PATCH 25/29] Re-add getSelectedSensorVelocity --- .../org/team1540/rooster/wrappers/ChickenController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java index a650b7c6..558a3d3a 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java @@ -493,4 +493,11 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, * @return Error Code generated by function. 0 indicates no error. */ ErrorCode setStatusFramePeriod(StatusFrame frame, int periodMs); + + /** + * Get the selected sensor velocity. + * + * @return Velocity of selected sensor in raw sensor units per decisecond. + */ + int getSelectedSensorVelocity(); } From d256c1690be00f0458958f10f925934881319198 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Mon, 7 Jan 2019 18:58:49 -0800 Subject: [PATCH 26/29] Update Travis deploy scripts --- .travis.yml | 4 ++-- travis-scripts/name-release-files.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) mode change 100755 => 100644 travis-scripts/name-release-files.sh diff --git a/.travis.yml b/.travis.yml index 417c2421..79dcc428 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ env: before_deploy: travis-scripts/name-release-files.sh deploy: - provider: pages # Javadoc deployment to Github Pages - local-dir: lib/build/docs/javadoc/ + local-dir: build/docs/javadoc/ skip-cleanup: true github-token: $GITHUB_TOKEN keep-history: true @@ -31,7 +31,7 @@ deploy: - provider: releases # Automatic JAR file deployment whenever a release is created api_key: $GITHUB_TOKEN file-glob: true - file: lib/build/libs/* + file: build/libs/* skip-cleanup: true on: tags: true diff --git a/travis-scripts/name-release-files.sh b/travis-scripts/name-release-files.sh old mode 100755 new mode 100644 index eefec78c..25f8666c --- a/travis-scripts/name-release-files.sh +++ b/travis-scripts/name-release-files.sh @@ -5,7 +5,7 @@ set -ev # check if $TRAVIS_TAG is set-if so, rename lib's output jars to make sense in preparation for uploading them to Github if [ -n "$TRAVIS_TAG" ] then - mv lib/build/libs/lib.jar "lib/build/libs/ROOSTER-$TRAVIS_TAG.jar" - mv lib/build/libs/lib-sources.jar "lib/build/libs/ROOSTER-$TRAVIS_TAG-sources.jar" - mv lib/build/libs/lib-javadoc.jar "lib/build/libs/ROOSTER-$TRAVIS_TAG-javadoc.jar" + mv lib/build/libs/lib.jar "build/libs/ROOSTER-$TRAVIS_TAG.jar" + mv lib/build/libs/lib-sources.jar "build/libs/ROOSTER-$TRAVIS_TAG-sources.jar" + mv lib/build/libs/lib-javadoc.jar "build/libs/ROOSTER-$TRAVIS_TAG-javadoc.jar" fi From 2e72e1ea890aeec85a520226bbd10d5081975b54 Mon Sep 17 00:00:00 2001 From: Zachary Robinson Date: Mon, 7 Jan 2019 19:02:26 -0800 Subject: [PATCH 27/29] Fix some Travis caching Maybe not all, unsure --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 79dcc428..297bfd93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ jdk: before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - - rm -f $HOME/.gradle/caches/4.4/fileHashes/fileHashes.bin - - rm -f $HOME/.gradle/caches/4.4/fileHashes/fileHashes.lock + - rm -f $HOME/.gradle/caches/5.0/fileHashes/fileHashes.bin + - rm -f $HOME/.gradle/caches/5.0/fileHashes/fileHashes.lock cache: directories: - "$HOME/.gradle/caches/" From aa1ba97ae0b604cd6190d57c1a85f4761e2e7cfe Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 7 Jan 2019 19:05:42 -0800 Subject: [PATCH 28/29] Re-add getSelectedSensorPosition() Also moved them back to their original position and fixed some doc. --- .../rooster/wrappers/ChickenController.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java index 558a3d3a..931720bb 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java @@ -413,6 +413,20 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ double getIntegralAccumulator(); + /** + * Get the selected sensor position. + * + * @return Position of selected sensor in raw sensor units. + */ + int getSelectedSensorPosition(); + + /** + * Get the selected sensor velocity. + * + * @return Velocity of selected sensor in raw sensor units per decisecond. + */ + int getSelectedSensorVelocity(); + int getStatusFramePeriod(StatusFrameEnhanced frame); /** @@ -493,11 +507,4 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, * @return Error Code generated by function. 0 indicates no error. */ ErrorCode setStatusFramePeriod(StatusFrame frame, int periodMs); - - /** - * Get the selected sensor velocity. - * - * @return Velocity of selected sensor in raw sensor units per decisecond. - */ - int getSelectedSensorVelocity(); } From dfb3df66035c0f26d4ec1f1605dba9bd68cc876e Mon Sep 17 00:00:00 2001 From: edelmanjm Date: Mon, 7 Jan 2019 19:08:57 -0800 Subject: [PATCH 29/29] Re-add setSelectedSensorPosition() --- .../rooster/wrappers/ChickenController.java | 17 +++++++++++++++++ .../rooster/wrappers/ChickenVictor.java | 1 + 2 files changed, 18 insertions(+) diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java index 931720bb..698482c2 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenController.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenController.java @@ -490,6 +490,23 @@ ErrorCode configReverseLimitSwitchSource(RemoteLimitSwitchSource type, */ ErrorCode setIntegralAccumulator(double iaccum); + /** + * Sets the sensor position to the given value. + * + * @param sensorPos Position to set for the selected sensor (in Raw Sensor Units). + * @param pidIdx The PID IDX to use. + * @return Error Code generated by function. 0 indicates no error. + */ + ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx); + + /** + * Sets the sensor position to the given value. + * + * @param sensorPos Position to set for the selected sensor (in Raw Sensor Units). + * @return Error Code generated by function. 0 indicates no error. + */ + ErrorCode setSelectedSensorPosition(int sensorPos); + /** * Sets the period of the given status frame. * diff --git a/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java index 40c9f1bc..4988c453 100644 --- a/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java +++ b/src/main/java/org/team1540/rooster/wrappers/ChickenVictor.java @@ -362,6 +362,7 @@ public ErrorCode setIntegralAccumulator(double iaccum) { return super.setIntegralAccumulator(iaccum, defaultPidIdx, defaultTimeoutMs); } + @Override public ErrorCode setSelectedSensorPosition(int sensorPos, int pidIdx) { return super.setSelectedSensorPosition(sensorPos, pidIdx, defaultTimeoutMs); }