diff --git a/.gitignore b/.gitignore
index aa724b7..08e7f17 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,5 @@
.externalNativeBuild
.cxx
local.properties
+
+/**/build
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index f9fd68f..91f47a9 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -10,6 +10,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index e483b6f..2731899 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,3 @@
-
diff --git a/build.gradle.kts b/build.gradle.kts
index f12339c..8db84cb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -12,8 +12,7 @@ plugins {
}
allprojects {
- version = property("version") as String
- group = "dev.nextftc"
+ group = "dev.nextftc.extensions"
}
subprojects {
@@ -23,6 +22,7 @@ subprojects {
scm {
fromGithub("NextFTC", "NextFTC")
}
+ license("GNU General Public License, version 3", "https://www.gnu.org/licenses/gpl-3.0.html")
developer("Davis Luxenberg", "davis.luxenberg@outlook.com", url = "https://github.com/BeepBot99")
developer("Rowan McAlpin", "rowan@nextftc.dev", url = "https://rowanmcalpin.com")
developer("Zach Harel", "ftc@zharel.me", url = "https://github.com/zachwaffle4")
diff --git a/gradle.properties b/gradle.properties
index f2c995c..a048aa9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,5 +2,9 @@ kotlin.code.style=official
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
-version=1.0.0
-automaticMavenCentralSync=true
\ No newline at end of file
+dev.nextftc.publishing.automaticMavenCentralSync=true
+
+android.useAndroidX=true
+
+versions.roadrunner=1.0.0
+versions.pedro=1.0.0
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d123e5e..07ba9dd 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,10 +1,12 @@
[versions]
kotlin = "2.0.0"
android = "8.7.3"
-nextftc = "1.0.0-SNAPSHOT"
+nextftc = "1.0.0-beta.2"
kotest = "5.9.1"
ftc = "10.3.0"
pedro = "2.0.0"
+rr-core = "1.0.1"
+rr-ftc = "0.1.25"
[libraries]
kotest-runner = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
@@ -18,11 +20,14 @@ ftc-common = { group = "org.firstinspires.ftc", name = "FtcCommon", version.ref
nextftc-ftc = { group = "dev.nextftc", name = "ftc", version.ref = "nextftc" }
nextftc-hardware = { group = "dev.nextftc", name = "hardware", version.ref = "nextftc" }
pedro = { module = "com.pedropathing:ftc", version.ref = "pedro" }
+rr-core = { module = "com.acmerobotics.roadrunner:core", version.ref = "rr-core" }
+rr-ftc = { module = "com.acmerobotics.roadrunner:ftc", version.ref = "rr-ftc" }
[bundles]
kotest = ["kotest-runner", "kotest-assertations", "kotest-property"]
ftc = ["ftc-robot-core", "ftc-hardware", "ftc-common"]
nextftc = ["nextftc-ftc", "nextftc-hardware"]
+roadrunner = ["rr-core", "rr-ftc"]
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
diff --git a/pedro/build.gradle.kts b/pedro/build.gradle.kts
index 3e4f606..af23659 100644
--- a/pedro/build.gradle.kts
+++ b/pedro/build.gradle.kts
@@ -30,15 +30,13 @@ dependencies {
implementation(libs.bundles.nextftc)
implementation(libs.pedro)
compileOnly(libs.bundles.ftc)
-
- testImplementation(libs.bundles.kotest)
- testImplementation(libs.mockk)
}
+version = property("versions.pedro") as String
description =
- "The hardware library for NextFTC, a user-friendly library for FTC. Includes hardware interfaces, wrapper implementations, and hardware commands."
+ "NextFTC's extension to add support for Pedro Pathing."
nextFTCPublishing {
- displayName = "NextFTC Hardware"
+ displayName = "NextFTC Extensions - Pedro"
logoPath = "../assets/logo-icon.svg"
}
\ No newline at end of file
diff --git a/pedro/src/androidTest/java/dev/nextftc/extensions/ExampleInstrumentedTest.kt b/pedro/src/androidTest/java/dev/nextftc/extensions/ExampleInstrumentedTest.kt
deleted file mode 100644
index b7382bc..0000000
--- a/pedro/src/androidTest/java/dev/nextftc/extensions/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package dev.nextftc.extensions
-
-import android.support.test.InstrumentationRegistry
-import android.support.test.runner.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("dev.nextftc.extensions.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/FollowPath.kt b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/FollowPath.kt
new file mode 100644
index 0000000..6ae50a9
--- /dev/null
+++ b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/FollowPath.kt
@@ -0,0 +1,37 @@
+package dev.nextftc.extensions.pedro
+
+import com.pedropathing.paths.Path
+import com.pedropathing.paths.PathChain
+import dev.nextftc.core.commands.Command
+import dev.nextftc.extensions.pedro.PedroComponent.Companion.follower
+
+class FollowPath @JvmOverloads constructor(
+ private val path: PathChain,
+ private val holdEnd: Boolean? = null,
+ private val maxPower: Double? = null
+) : Command() {
+
+ init {
+ require(maxPower == null || holdEnd != null) { "If maxPower is passed, holdEnd must be passed as well." }
+ }
+
+ @JvmOverloads
+ constructor(path: Path, holdEnd: Boolean? = null, maxPower: Double? = null) : this(
+ PathChain(path),
+ holdEnd,
+ maxPower
+ )
+
+ override val isDone: Boolean
+ get() = !follower.isBusy
+
+ override fun start() {
+ if (holdEnd !== null && maxPower !== null) follower.followPath(path, maxPower, holdEnd)
+ else if (holdEnd !== null) follower.followPath(path, holdEnd)
+ else follower.followPath(path)
+ }
+
+ override fun stop(interrupted: Boolean) {
+ if (interrupted) follower.breakFollowing()
+ }
+}
\ No newline at end of file
diff --git a/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroComponent.kt b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroComponent.kt
new file mode 100644
index 0000000..3f3800c
--- /dev/null
+++ b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroComponent.kt
@@ -0,0 +1,37 @@
+package dev.nextftc.extensions.pedro
+
+import com.pedropathing.follower.Follower
+import com.qualcomm.robotcore.hardware.HardwareMap
+import dev.nextftc.core.components.Component
+import dev.nextftc.core.units.Angle
+import dev.nextftc.core.units.rad
+import dev.nextftc.ftc.ActiveOpMode
+import java.util.function.Supplier
+
+class PedroComponent(private val followerFactory: (HardwareMap) -> Follower) : Component {
+
+ override fun preInit() {
+ _follower = followerFactory(ActiveOpMode.hardwareMap)
+ }
+
+ override fun preWaitForStart() = follower.update()
+ override fun preUpdate() = follower.update()
+
+ override fun postStop() {
+ _follower = null
+ }
+
+ companion object {
+ private var _follower: Follower? = null
+
+ @get:JvmName("follower")
+ @JvmStatic
+ val follower: Follower
+ get() = _follower ?: error("Follower not initialized! Make sure you added PedroComponent to your OpMode.")
+
+ @get:JvmName("gyro")
+ @JvmStatic
+ val gyro: Supplier = Supplier { follower.totalHeading.rad.normalized }
+
+ }
+}
\ No newline at end of file
diff --git a/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroDriverControlled.kt b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroDriverControlled.kt
new file mode 100644
index 0000000..2de3c92
--- /dev/null
+++ b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/PedroDriverControlled.kt
@@ -0,0 +1,26 @@
+package dev.nextftc.extensions.pedro
+
+import dev.nextftc.extensions.pedro.PedroComponent.Companion.follower
+import dev.nextftc.hardware.driving.DriverControlledCommand
+import java.util.function.Supplier
+
+class PedroDriverControlled @JvmOverloads constructor(
+ drivePower: Supplier,
+ strafePower: Supplier,
+ turnPower: Supplier,
+ private val robotCentric: Boolean = true
+) : DriverControlledCommand(drivePower, strafePower, turnPower) {
+
+ override fun start() {
+ follower.startTeleopDrive()
+ }
+
+ override fun calculateAndSetPowers(powers: DoubleArray) {
+ val (drive, strafe, turn) = powers
+ follower.setTeleOpDrive(drive, strafe, turn, robotCentric)
+ }
+
+ override fun stop(interrupted: Boolean) {
+ if (interrupted) follower.breakFollowing()
+ }
+}
\ No newline at end of file
diff --git a/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnBy.kt b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnBy.kt
new file mode 100644
index 0000000..b29227e
--- /dev/null
+++ b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnBy.kt
@@ -0,0 +1,20 @@
+package dev.nextftc.extensions.pedro
+
+import dev.nextftc.core.commands.Command
+import dev.nextftc.core.units.Angle
+import dev.nextftc.core.units.abs
+import dev.nextftc.extensions.pedro.PedroComponent.Companion.follower
+
+class TurnBy(private val angle: Angle) : Command() {
+
+ init {
+ named("TurnBy($angle)")
+ }
+
+ override val isDone: Boolean
+ get() = !follower.isTurning
+
+ override fun start() {
+ follower.turn(abs(angle).inRad, angle.sign > 0)
+ }
+}
\ No newline at end of file
diff --git a/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnTo.kt b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnTo.kt
new file mode 100644
index 0000000..7f82460
--- /dev/null
+++ b/pedro/src/main/kotlin/dev/nextftc/extensions/pedro/TurnTo.kt
@@ -0,0 +1,19 @@
+package dev.nextftc.extensions.pedro
+
+import dev.nextftc.core.commands.Command
+import dev.nextftc.core.units.Angle
+import dev.nextftc.extensions.pedro.PedroComponent.Companion.follower
+
+class TurnTo(private val angle: Angle) : Command() {
+
+ init {
+ named("TurnTo($angle)")
+ }
+
+ override val isDone: Boolean
+ get() = !follower.isTurning
+
+ override fun start() {
+ follower.turnTo(angle.inRad)
+ }
+}
\ No newline at end of file
diff --git a/pedro/src/test/java/dev/nextftc/extensions/ExampleUnitTest.kt b/pedro/src/test/java/dev/nextftc/extensions/ExampleUnitTest.kt
deleted file mode 100644
index 2abe954..0000000
--- a/pedro/src/test/java/dev/nextftc/extensions/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package dev.nextftc.extensions
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/roadrunner/build.gradle.kts b/roadrunner/build.gradle.kts
new file mode 100644
index 0000000..5043733
--- /dev/null
+++ b/roadrunner/build.gradle.kts
@@ -0,0 +1,42 @@
+plugins {
+ kotlin("android")
+ id("com.android.library")
+}
+
+android {
+ namespace = "dev.nextftc.extensions.roadrunner"
+ compileSdk = 35
+
+ defaultConfig {
+ minSdk = 24
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = "1.8"
+ freeCompilerArgs += "-Xjvm-default=all"
+ }
+
+ publishing {
+ singleVariant("release")
+ }
+}
+
+dependencies {
+ implementation(libs.bundles.nextftc)
+ implementation(libs.bundles.roadrunner)
+ implementation(libs.bundles.ftc)
+}
+
+description =
+ "Support for using RoadRunner with NextFTC."
+
+nextFTCPublishing {
+ displayName = "NextFTC Extensions - RoadRunner"
+ logoPath = "../assets/logo-icon.svg"
+ version = property("versions.roadrunner") as String
+}
\ No newline at end of file
diff --git a/roadrunner/src/main/kotlin/dev/nextftc/extensions/roadrunner/FollowTrajectory.kt b/roadrunner/src/main/kotlin/dev/nextftc/extensions/roadrunner/FollowTrajectory.kt
new file mode 100644
index 0000000..6214e30
--- /dev/null
+++ b/roadrunner/src/main/kotlin/dev/nextftc/extensions/roadrunner/FollowTrajectory.kt
@@ -0,0 +1,47 @@
+package dev.nextftc.extensions.roadrunner
+
+import com.acmerobotics.roadrunner.Pose2dDual
+import com.acmerobotics.roadrunner.PoseVelocity2d
+import com.acmerobotics.roadrunner.Time
+import com.acmerobotics.roadrunner.TimeTrajectory
+import com.acmerobotics.roadrunner.Trajectory
+import com.acmerobotics.roadrunner.Vector2d
+import com.qualcomm.robotcore.util.ElapsedTime
+import dev.nextftc.core.commands.Command
+
+/**
+ * Follows a trajectory parametrized by time.
+ *
+ * Note that the output of [TrajectoryCommandBuilder.build] only returns a [Command]
+ * object and not a [FollowTrajectory] object,
+ * because the builder sequences the trajectory with other actions,
+ * such as [Turn]s, [dev.nextftc.core.commands.delays.Delay]s or user-specified callbacks.
+ */
+class FollowTrajectory(private val mecanumDrive: NextFTCMecanumDrive, private val trajectory: TimeTrajectory) : Command() {
+ constructor(mecanumDrive: NextFTCMecanumDrive, trajectory: Trajectory) :
+ this(mecanumDrive, TimeTrajectory(trajectory))
+
+ val timer = ElapsedTime()
+
+ override val isDone: Boolean
+ get() = timer.seconds() >= trajectory.duration
+
+ override fun start() {
+ timer.reset()
+ }
+
+ override fun update() {
+ val txWorldTarget: Pose2dDual