Skip to content

Commit ec737f5

Browse files
authored
Merge pull request #10 from NextFTC/revert-9-revert-8-main
Revert "Revert "v1 :)""
2 parents 3c0b895 + 8190118 commit ec737f5

816 files changed

Lines changed: 8417 additions & 107379 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.DS_Store

6 KB
Binary file not shown.

.gca/actions/ActionGroups.kt

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.acmerobotics.roadrunner.actions
2+
3+
import com.acmerobotics.dashboard.canvas.Canvas
4+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket
5+
6+
/**
7+
* Action combinator that executes the action group [initialActions] in series. Each action is run one after the other.
8+
* When an action completes, the next one is immediately run. This action completes when the last action completes.
9+
*/
10+
data class SequentialAction(
11+
val initialActions: List<Action>
12+
) : Action {
13+
var actions = initialActions.flatMap {
14+
if (it is SequentialAction) it.initialActions else listOf(it)
15+
}
16+
private set
17+
18+
constructor(vararg actions: Action) : this(actions.asList())
19+
20+
override tailrec fun run(p: TelemetryPacket): Boolean {
21+
if (actions.isEmpty()) {
22+
return false
23+
}
24+
25+
return if (actions.first().run(p)) {
26+
true
27+
} else {
28+
actions = actions.drop(1)
29+
run(p)
30+
}
31+
}
32+
33+
override fun preview(fieldOverlay: Canvas) {
34+
for (a in initialActions) {
35+
a.preview(fieldOverlay)
36+
}
37+
}
38+
}
39+
40+
/**
41+
* Action combinator that executes the action group [initialActions] in parallel. Each call to [run] on this action
42+
* calls [run] on _every_ live child action in the order provided. Completed actions are removed from the rotation
43+
* and _do not_ prevent the completion of other actions. This action completes when all of [initialActions] have.
44+
*/
45+
data class ParallelAction(
46+
val initialActions: List<Action>
47+
) : Action {
48+
var actions = initialActions.flatMap {
49+
if (it is ParallelAction) it.initialActions else listOf(it)
50+
}
51+
private set
52+
53+
constructor(vararg actions: Action) : this(actions.asList())
54+
55+
override fun run(p: TelemetryPacket): Boolean {
56+
actions = actions.filter { it.run(p) }
57+
return actions.isNotEmpty()
58+
}
59+
60+
override fun preview(fieldOverlay: Canvas) {
61+
for (a in initialActions) {
62+
a.preview(fieldOverlay)
63+
}
64+
}
65+
}
66+
67+
/**
68+
* Action combinator that executes the action group [initialActions] in parallel. Each call to [run] on this action
69+
* calls [run] on _every_ live child action in the order provided. Once one action ends, all other actions are ended.
70+
*/
71+
class RaceAction(
72+
initialActions: List<Action>
73+
) : Action {
74+
var actions = initialActions
75+
private set
76+
var interrupting = false
77+
78+
constructor(vararg actions: Action) : this(actions.asList())
79+
80+
override fun run(p: TelemetryPacket): Boolean {
81+
val remaining = actions.filter { it.run(p) }
82+
if (interrupting) {
83+
return remaining.isNotEmpty()
84+
} else if (actions.size != remaining.size) {
85+
interrupting = true
86+
actions = remaining.filter { it is Interruptible }
87+
.map { (it as Interruptible).onInterrupt() }
88+
}
89+
return true
90+
}
91+
92+
override fun preview(fieldOverlay: Canvas) {
93+
for (a in actions) {
94+
a.preview(fieldOverlay)
95+
}
96+
}
97+
}

.gca/actions/Actions.kt

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.acmerobotics.roadrunner.actions
2+
3+
import com.acmerobotics.dashboard.canvas.Canvas
4+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket
5+
import kotlin.time.ComparableTimeMark
6+
import kotlin.time.Duration
7+
import kotlin.time.Duration.Companion.nanoseconds
8+
import kotlin.time.Duration.Companion.seconds
9+
import kotlin.time.DurationUnit
10+
import kotlin.time.TimeSource
11+
import kotlin.time.TimeSource.Monotonic.markNow
12+
import kotlin.time.toJavaDuration
13+
import kotlin.time.toKotlinDuration
14+
15+
/**
16+
* Concurrent task for cooperative multitasking with some FTC dashboard hooks. Actions may have a mutable state.
17+
*/
18+
@JvmDefaultWithoutCompatibility
19+
fun interface Action {
20+
/**
21+
* Runs a single uninterruptible block. Returns true if the action should run again and false if it has completed.
22+
* A telemetry packet [p] is provided to record any information on the action's progress.
23+
*/
24+
fun run(p: TelemetryPacket): Boolean
25+
26+
/**
27+
* Draws a preview of the action on canvas [fieldOverlay].
28+
*/
29+
fun preview(fieldOverlay: Canvas) {}
30+
31+
/**
32+
* The action's requirements (optional).
33+
* It is up to the action queue to resolve requirements.
34+
*/
35+
val requirements: Set<Any> get() = emptySet()
36+
37+
/**
38+
* Returns a new action that executes this action followed by [a].
39+
*/
40+
fun then(a: Action) = SequentialAction(this, a)
41+
fun then(f: InstantFunction) = SequentialAction(this, InstantAction(f))
42+
fun then(a: () -> Action) = SequentialAction(this, a())
43+
44+
/**
45+
* Returns a new action that executes this action in parallel with [a].
46+
*/
47+
fun with(a: Action) = ParallelAction(this, a)
48+
fun with(f: InstantFunction) = ParallelAction(this, InstantAction(f))
49+
fun with(a: () -> Action) = ParallelAction(this, a())
50+
51+
/**
52+
* Returns a new action that executes this action in parallel with [a].
53+
*/
54+
fun race(a: Action) = RaceAction(this, a)
55+
fun race(f: InstantFunction) = RaceAction(this, InstantAction(f))
56+
fun race(a: () -> Action) = RaceAction(this, a())
57+
58+
/**
59+
* Returns a new action that waits [dt] seconds before executing this action.
60+
*/
61+
fun delay(dt: Double) = SleepAction(dt).then(this)
62+
63+
/**
64+
* Returns an interruptible copy of this action, with [onInterruption] occurring on interrupt.
65+
*/
66+
fun interruptible(onInterruption: Action) = object : Interruptible {
67+
override fun onInterrupt(): Action = onInterruption
68+
69+
override fun run(p: TelemetryPacket): Boolean = this@Action.run(p)
70+
71+
override fun preview(fieldOverlay: Canvas) = this@Action.preview(fieldOverlay)
72+
}
73+
fun interruptible(onInterruption: InstantFunction) = interruptible(InstantAction(onInterruption))
74+
fun interruptible(onInterruption: () -> Action) = interruptible(onInterruption())
75+
}
76+
77+
open class ActionEx @JvmOverloads constructor(
78+
private val initBlock: (TelemetryPacket) -> Unit = { },
79+
private val loopBlock: (TelemetryPacket) -> Boolean = { false },
80+
private val endBlock: (TelemetryPacket) -> Unit = { }
81+
) : Action {
82+
83+
open fun init(packet: TelemetryPacket) = initBlock(packet)
84+
open fun loop(packet: TelemetryPacket) = loopBlock(packet)
85+
open fun end(packet: TelemetryPacket) = endBlock(packet)
86+
87+
private val sequential = SequentialAction(
88+
{ init(it).let { false } },
89+
{ loop(it) },
90+
{ end(it).let { false } }
91+
)
92+
93+
final override fun run(packet: TelemetryPacket) = sequential.run(packet)
94+
95+
fun withInit(initBlock: (TelemetryPacket) -> Unit) = ActionEx(initBlock, loopBlock, endBlock)
96+
fun withLoop(loopBlock: (TelemetryPacket) -> Boolean) = ActionEx(initBlock, loopBlock, endBlock)
97+
fun withEnd(endBlock: (TelemetryPacket) -> Unit) = ActionEx(initBlock, loopBlock, endBlock)
98+
}
99+
100+
/**
101+
* Utility object for action-related functionality.
102+
*/
103+
object Actions {
104+
/**
105+
* Returns the current time in seconds.
106+
*/
107+
@JvmStatic fun now() = System.nanoTime().nanoseconds.toDouble(DurationUnit.SECONDS)
108+
}
109+
110+
/**
111+
* Primitive sleep action that stalls for [dt].
112+
*/
113+
data class SleepAction(val dt: Duration) : ActionEx() {
114+
constructor(dt: java.time.Duration) : this(dt.toKotlinDuration())
115+
constructor(dt: Double) : this(dt.seconds)
116+
117+
private lateinit var start: ComparableTimeMark
118+
119+
override fun init(packet: TelemetryPacket) {
120+
start = markNow()
121+
}
122+
123+
override fun loop(packet: TelemetryPacket) = (start.elapsedNow() - dt).isPositive()
124+
}
125+
fun interface InstantFunction {
126+
fun run()
127+
}
128+
129+
/**
130+
* Instant action that executes [f] immediately.
131+
*/
132+
class InstantAction(val f: InstantFunction) : Action {
133+
override fun run(p: TelemetryPacket): Boolean {
134+
f.run()
135+
return false
136+
}
137+
}
138+
139+
/**
140+
* Null action that does nothing.
141+
*/
142+
class NullAction : Action {
143+
override fun run(p: TelemetryPacket) = false
144+
}
145+
146+
/**
147+
* An action that can be interrupted, providing a specific action to execute upon interruption.
148+
*/
149+
interface Interruptible : Action {
150+
151+
/**
152+
* Returns the action to execute upon interruption.
153+
*/
154+
fun onInterrupt(): Action
155+
}

.gca/actions/ActionsFtc.kt

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
@file:JvmName("Actions")
2+
3+
package com.acmerobotics.roadrunner.ftc
4+
5+
import android.content.Context
6+
import com.acmerobotics.dashboard.FtcDashboard
7+
import com.acmerobotics.dashboard.canvas.Canvas
8+
import com.acmerobotics.dashboard.telemetry.TelemetryPacket
9+
import com.acmerobotics.roadrunner.actions.Action
10+
import com.acmerobotics.roadrunner.actions.Interruptible
11+
import com.qualcomm.ftccommon.FtcEventLoop
12+
import com.qualcomm.ftccommon.FtcRobotControllerSettingsActivity
13+
import com.qualcomm.robotcore.eventloop.opmode.OpMode
14+
import com.qualcomm.robotcore.eventloop.opmode.OpModeManagerNotifier
15+
import org.firstinspires.ftc.ftccommon.external.OnCreateEventLoop
16+
17+
/**
18+
* Run [a] to completion in a blocking loop.
19+
*/
20+
fun runBlocking(a: Action) {
21+
val dash = FtcDashboard.getInstance()
22+
val c = Canvas()
23+
a.preview(c)
24+
25+
var b = true
26+
while (b && !Thread.currentThread().isInterrupted) {
27+
val p = TelemetryPacket()
28+
p.fieldOverlay().operations.addAll(c.operations)
29+
30+
b = a.run(p)
31+
32+
dash.sendTelemetryPacket(p)
33+
}
34+
}
35+
36+
/**
37+
* Singleton object responsible for managing and updating a queue of concurrent asynchronous tasks (actions).
38+
* Provides methods to enqueue actions and process the queue during the robot's main loop.
39+
*
40+
* Implements `OpModeManagerNotifier.Notifications` to manage the actions' lifecycle across different OpMode stages.
41+
*/
42+
object ActionRunner : OpModeManagerNotifier.Notifications {
43+
private val dash = lazy { FtcDashboard.getInstance() }
44+
private val _actions = ArrayDeque<Action>()
45+
46+
/**
47+
* The actions currently being run.
48+
*/
49+
@JvmStatic
50+
@get:JvmName("actions")
51+
val actions: List<Action>
52+
get() = _actions
53+
54+
/**
55+
* Adds [action] to the run queue.
56+
*/
57+
@JvmStatic
58+
fun run(action: Action) {
59+
val used = _actions.filter { it.requirements.any { req -> req in action.requirements} }
60+
val triggerred = used.mapNotNull { when (it) {
61+
is Interruptible -> it.onInterrupt()
62+
else -> null
63+
} }
64+
_actions.removeAll(used)
65+
_actions.addAll(triggerred)
66+
_actions.addLast(action)
67+
}
68+
69+
/**
70+
* Adds all actions in [actions] to the run queue.
71+
*/
72+
@JvmStatic
73+
fun run(actions: Collection<Action>) {
74+
actions.forEach(::run)
75+
}
76+
77+
/**
78+
* Adds all actions in [actions] to the run queue.
79+
*/
80+
@JvmStatic
81+
fun run(vararg actions: Action) {
82+
actions.forEach(::run)
83+
}
84+
85+
/**
86+
* Updates the run queue.
87+
* MUST be called at the end of every loop.
88+
*/
89+
@JvmStatic
90+
fun update() {
91+
val p = TelemetryPacket()
92+
_actions.retainAll {
93+
it.run(p)
94+
}
95+
dash.value.sendTelemetryPacket(p)
96+
}
97+
98+
@OnCreateEventLoop
99+
@JvmStatic
100+
fun register(context: Context, eventLoop: FtcEventLoop) {
101+
eventLoop.opModeManager.registerListener(this)
102+
}
103+
104+
override fun onOpModePreInit(p0: OpMode?) {
105+
require(dash.isInitialized())
106+
require(_actions.isEmpty())
107+
}
108+
109+
override fun onOpModePreStart(p0: OpMode?) {}
110+
111+
override fun onOpModePostStop(p0: OpMode?) {
112+
_actions.clear()
113+
}
114+
}

0 commit comments

Comments
 (0)