[cmd3] Change Command.noRequirements to accept a command implementation (#8783)

This is more ergonomic than `Command.noRequirements().executing(...)`
This commit is contained in:
Sam Carlberg
2026-04-19 18:40:50 -04:00
committed by GitHub
parent 3eaeac6150
commit b7df267687
19 changed files with 121 additions and 172 deletions

View File

@@ -85,7 +85,7 @@ import org.wpilib.units.measure.Time;
* // to run when not in use. Interrupting one of the inner commands while it's
* // running will cancel the entire sequence.
* private Command advancedScoringSequence() {
* return Command.noRequirements().executing(coroutine -> {
* return Command.noRequirements(coroutine -> {
* coroutine.await(drivetrain.driveToScoringLocation());
* coroutine.await(elevator.moveToScoringHeight());
* coroutine.await(gripper.release());
@@ -226,15 +226,17 @@ public interface Command {
* Creates a command that does not require any hardware; that is, it does not affect the state of
* any physical objects. This is useful for commands that do some cleanup or state management,
* such as resetting odometry or sensors, that you don't want to interrupt a command that's
* controlling the mechanisms it affects.
* controlling the mechanisms it affects, or for a command composition that you don't want to
* inherit the requirements of its child commands.
*
* <p>More configuration options are needed after calling this function before the command can be
* created. See {@link StagedCommandBuilder} for details.
*
* @param body The command's body. Cannot be null.
* @return a builder that can be used to configure the resulting command
*/
static NeedsExecutionBuilderStage noRequirements() {
return new StagedCommandBuilder().noRequirements();
static NeedsNameBuilderStage noRequirements(Consumer<Coroutine> body) {
return new StagedCommandBuilder().noRequirements().executing(body);
}
/**
@@ -326,7 +328,7 @@ public interface Command {
static NeedsNameBuilderStage waitUntil(BooleanSupplier condition) {
requireNonNullParam(condition, "condition", "Command.waitUntil");
return noRequirements().executing(coroutine -> coroutine.waitUntil(condition));
return noRequirements(coroutine -> coroutine.waitUntil(condition));
}
/**
@@ -339,7 +341,7 @@ public interface Command {
static NeedsNameBuilderStage waitFor(Time duration) {
requireNonNullParam(duration, "duration", "Command.waitFor");
return noRequirements().executing(coroutine -> coroutine.wait(duration));
return noRequirements(coroutine -> coroutine.wait(duration));
}
/**

View File

@@ -79,7 +79,7 @@ public final class Coroutine {
*
* <pre>{@code
* Command example() {
* return Command.noRequirements().executing(coroutine -> {
* return Command.noRequirements(coroutine -> {
* Command child = ...;
* coroutine.fork(child);
* // ... do more things
@@ -121,7 +121,7 @@ public final class Coroutine {
*
* <pre>{@code
* Command example() {
* return Command.noRequirements().executing(coroutine -> {
* return Command.noRequirements(coroutine -> {
* Collection<Command> innerCommands = ...;
* coroutine.fork(innerCommands);
* // ... do more things

View File

@@ -34,7 +34,7 @@ import org.wpilib.units.measure.Time;
* canceled when the enclosing command exits.
*
* <pre>{@code
* Command shootWhileAiming = Command.noRequirements().executing(co -> {
* Command shootWhileAiming = Command.noRequirements(co -> {
* turret.atTarget.onTrue(shooter.shootOnce());
* co.await(turret.lockOnGoal());
* }).named("Shoot While Aiming");

View File

@@ -27,8 +27,7 @@ class BindingScopeTest extends SchedulerTest {
BindingScope[] scopeRef = new BindingScope[1];
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
coroutine -> {
BindingScope scope = BindingScope.createNarrowestScope(m_scheduler);
scopeRef[0] = scope;
@@ -49,8 +48,7 @@ class BindingScopeTest extends SchedulerTest {
BindingScope[] scopeRef = new BindingScope[1];
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
coroutine -> {
BindingScope scope = BindingScope.createNarrowestScope(m_scheduler);
scopeRef[0] = scope;

View File

@@ -24,8 +24,7 @@ class CoroutineTest extends CommandTestBase {
var c = new NullCommand();
var all =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.fork(a, b, c);
co.park();
@@ -45,8 +44,7 @@ class CoroutineTest extends CommandTestBase {
AtomicInteger i = new AtomicInteger(0);
var yieldInSynchronized =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
while (true) {
synchronized (mutex) {
@@ -68,8 +66,7 @@ class CoroutineTest extends CommandTestBase {
AtomicInteger i = new AtomicInteger(0);
var yieldInLock =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
while (true) {
lock.lock();
@@ -93,8 +90,7 @@ class CoroutineTest extends CommandTestBase {
AtomicReference<Runnable> escapeeCallback = new AtomicReference<>();
var badCommand =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
escapeeCallback.set(co::yield);
})
@@ -111,12 +107,10 @@ class CoroutineTest extends CommandTestBase {
@SuppressWarnings("CoroutineMayNotBeInScope")
void usingParentCoroutineInChildThrows() {
var parent =
Command.noRequirements()
.executing(
Command.noRequirements(
parentCoroutine -> {
parentCoroutine.await(
Command.noRequirements()
.executing(
Command.noRequirements(
childCoroutine -> {
parentCoroutine.yield();
})
@@ -135,10 +129,9 @@ class CoroutineTest extends CommandTestBase {
AtomicBoolean secondRan = new AtomicBoolean(false);
AtomicBoolean ranAfterAwait = new AtomicBoolean(false);
var firstInner = Command.noRequirements().executing(c2 -> firstRan.set(true)).named("First");
var firstInner = Command.noRequirements(c2 -> firstRan.set(true)).named("First");
var secondInner =
Command.noRequirements()
.executing(
Command.noRequirements(
c2 -> {
secondRan.set(true);
c2.park();
@@ -146,8 +139,7 @@ class CoroutineTest extends CommandTestBase {
.named("Second");
var outer =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.awaitAny(firstInner, secondInner);

View File

@@ -26,8 +26,8 @@ class ParallelGroupBuilderTest {
@Test
void optionalPopulatesOptionalOnly() {
var a = Command.noRequirements().executing(Coroutine::park).named("A");
var b = Command.noRequirements().executing(Coroutine::park).named("B");
var a = Command.noRequirements(Coroutine::park).named("A");
var b = Command.noRequirements(Coroutine::park).named("B");
var group = new ParallelGroupBuilder().optional(a, b).withAutomaticName();
@@ -50,8 +50,8 @@ class ParallelGroupBuilderTest {
@Test
void requiringPopulatesRequiredOnly() {
var a = Command.noRequirements().executing(Coroutine::park).named("A");
var b = Command.noRequirements().executing(Coroutine::park).named("B");
var a = Command.noRequirements(Coroutine::park).named("A");
var b = Command.noRequirements(Coroutine::park).named("B");
var group = new ParallelGroupBuilder().requiring(a, b).withAutomaticName();
@@ -62,10 +62,10 @@ class ParallelGroupBuilderTest {
@Test
void mixedRequiredAndOptional() {
var reqA = Command.noRequirements().executing(Coroutine::park).named("ReqA");
var reqB = Command.noRequirements().executing(Coroutine::park).named("ReqB");
var optX = Command.noRequirements().executing(Coroutine::park).named("OptX");
var optY = Command.noRequirements().executing(Coroutine::park).named("OptY");
var reqA = Command.noRequirements(Coroutine::park).named("ReqA");
var reqB = Command.noRequirements(Coroutine::park).named("ReqB");
var optX = Command.noRequirements(Coroutine::park).named("OptX");
var optY = Command.noRequirements(Coroutine::park).named("OptY");
var group =
new ParallelGroupBuilder().requiring(reqA, reqB).optional(optX, optY).withAutomaticName();

View File

@@ -183,9 +183,9 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void automaticNameRace() {
var a = Command.noRequirements().executing(coroutine -> {}).named("A");
var b = Command.noRequirements().executing(coroutine -> {}).named("B");
var c = Command.noRequirements().executing(coroutine -> {}).named("C");
var a = Command.noRequirements(coroutine -> {}).named("A");
var b = Command.noRequirements(coroutine -> {}).named("B");
var c = Command.noRequirements(coroutine -> {}).named("C");
var group = new ParallelGroupBuilder().optional(a, b, c).withAutomaticName();
assertEquals("(A | B | C)", group.name());
@@ -193,9 +193,9 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void automaticNameAll() {
var a = Command.noRequirements().executing(coroutine -> {}).named("A");
var b = Command.noRequirements().executing(coroutine -> {}).named("B");
var c = Command.noRequirements().executing(coroutine -> {}).named("C");
var a = Command.noRequirements(coroutine -> {}).named("A");
var b = Command.noRequirements(coroutine -> {}).named("B");
var c = Command.noRequirements(coroutine -> {}).named("C");
var group = new ParallelGroupBuilder().requiring(a, b, c).withAutomaticName();
assertEquals("(A & B & C)", group.name());
@@ -203,9 +203,9 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void automaticNameDeadline() {
var a = Command.noRequirements().executing(coroutine -> {}).named("A");
var b = Command.noRequirements().executing(coroutine -> {}).named("B");
var c = Command.noRequirements().executing(coroutine -> {}).named("C");
var a = Command.noRequirements(coroutine -> {}).named("A");
var b = Command.noRequirements(coroutine -> {}).named("B");
var c = Command.noRequirements(coroutine -> {}).named("C");
var group = new ParallelGroupBuilder().requiring(a).optional(b, c).withAutomaticName();
assertEquals("[(A) * (B | C)]", group.name());

View File

@@ -84,7 +84,7 @@ class SchedulerCancellationTests extends CommandTestBase {
@Test
void cancelsEvictsOnDeck() {
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
m_scheduler.schedule(command);
m_scheduler.cancel(command);
assertFalse(m_scheduler.isScheduledOrRunning(command));
@@ -95,8 +95,7 @@ class SchedulerCancellationTests extends CommandTestBase {
var ranAfterCancel = new AtomicBoolean(false);
var commandRef = new AtomicReference<Command>(null);
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.scheduler().cancel(commandRef.get());
ranAfterCancel.set(true);
@@ -115,7 +114,7 @@ class SchedulerCancellationTests extends CommandTestBase {
@Test
void cancelAllEvictsOnDeck() {
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
m_scheduler.schedule(command);
m_scheduler.cancelAll();
assertFalse(m_scheduler.isScheduledOrRunning(command));
@@ -125,7 +124,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void cancelAllCancelsAll() {
var commands = new ArrayList<Command>(10);
for (int i = 1; i <= 10; i++) {
commands.add(Command.noRequirements().executing(Coroutine::yield).named("Command " + i));
commands.add(Command.noRequirements(Coroutine::yield).named("Command " + i));
}
commands.forEach(m_scheduler::schedule);
m_scheduler.run();
@@ -141,8 +140,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void cancelAllCallsOnCancelHookForRunningCommands() {
AtomicBoolean ranHook = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(Coroutine::park)
Command.noRequirements(Coroutine::park)
.whenCanceled(() -> ranHook.set(true))
.named("Command");
m_scheduler.schedule(command);
@@ -155,8 +153,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void cancelAllDoesNotCallOnCancelHookForQueuedCommands() {
AtomicBoolean ranHook = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(Coroutine::park)
Command.noRequirements(Coroutine::park)
.whenCanceled(() -> ranHook.set(true))
.named("Command");
m_scheduler.schedule(command);
@@ -209,20 +206,16 @@ class SchedulerCancellationTests extends CommandTestBase {
@Test
void cancelDeeplyNestedCompositions() {
Command root =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.await(
Command.noRequirements()
.executing(
Command.noRequirements(
co2 -> {
co2.await(
Command.noRequirements()
.executing(
Command.noRequirements(
co3 -> {
co3.await(
Command.noRequirements()
.executing(Coroutine::park)
Command.noRequirements(Coroutine::park)
.named("Park"));
})
.named("C3"));

View File

@@ -20,8 +20,7 @@ class SchedulerConflictTests extends CommandTestBase {
var mech = new Mechanism("The Mechanism", m_scheduler);
var group =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.awaitAll(
mech.run(Coroutine::park).named("First"),
@@ -64,8 +63,7 @@ class SchedulerConflictTests extends CommandTestBase {
.named("Second");
var group =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.fork(first);
co.fork(second);
@@ -88,25 +86,21 @@ class SchedulerConflictTests extends CommandTestBase {
void nestedOneShotCompositionsAllRunInOneCycle() {
var runs = new AtomicInteger(0);
Supplier<Command> makeOneShot =
() -> Command.noRequirements().executing(_c -> runs.incrementAndGet()).named("One Shot");
() -> Command.noRequirements(_c -> runs.incrementAndGet()).named("One Shot");
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.fork(makeOneShot.get());
co.fork(makeOneShot.get());
co.fork(
Command.noRequirements()
.executing(inner -> inner.fork(makeOneShot.get()))
Command.noRequirements(inner -> inner.fork(makeOneShot.get()))
.named("Inner"));
co.fork(
Command.noRequirements()
.executing(
Command.noRequirements(
co2 -> {
co2.fork(makeOneShot.get());
co2.fork(
Command.noRequirements()
.executing(
Command.noRequirements(
co3 -> {
co3.fork(makeOneShot.get());
})
@@ -130,7 +124,7 @@ class SchedulerConflictTests extends CommandTestBase {
// Child conflicts with and is lower priority than the Top command
// It should not be scheduled, and the parent command should exit immediately
var child = mechanism.run(Coroutine::park).named("Child");
var parent = Command.noRequirements().executing(co -> co.await(child)).named("Parent");
var parent = Command.noRequirements(co -> co.await(child)).named("Parent");
m_scheduler.schedule(top);
m_scheduler.schedule(parent);
@@ -149,7 +143,7 @@ class SchedulerConflictTests extends CommandTestBase {
// Child conflicts with and is higher priority than the Top command
// It should be scheduled, and the top command should be interrupted
var child = mechanism.run(Coroutine::park).named("Child");
var parent = Command.noRequirements().executing(co -> co.await(child)).named("Parent");
var parent = Command.noRequirements(co -> co.await(child)).named("Parent");
m_scheduler.schedule(top);
m_scheduler.schedule(parent);

View File

@@ -64,8 +64,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
var commandScopedCommand = mech.run(Coroutine::park).named("Command-Scoped Default Command");
var scopingCommand =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
mech.setDefaultCommand(commandScopedCommand);
co.park();
@@ -93,8 +92,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
var commandScopedCommand = mech.run(Coroutine::park).named("Command-Scoped Default Command");
var scopingCommand =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
mech.setDefaultCommand(commandScopedCommand);
co.park();
@@ -126,8 +124,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
var commandScopedCommand = mech.run(Coroutine::park).named("Command-Scoped Default Command");
final Command scopingCommand =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
mech.setDefaultCommand(commandScopedCommand);
co.park();

View File

@@ -43,17 +43,14 @@ class SchedulerErrorHandlingTests extends CommandTestBase {
@Test
void nestedErrorDetection() {
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.await(
Command.noRequirements()
.executing(
Command.noRequirements(
c2 -> {
new Trigger(m_scheduler, () -> true)
.onTrue(
Command.noRequirements()
.executing(
Command.noRequirements(
c3 -> {
// Throws IndexOutOfBoundsException
var unused = new ArrayList<>(0).get(-1);
@@ -95,10 +92,9 @@ class SchedulerErrorHandlingTests extends CommandTestBase {
@Test
void commandEncounteringErrorCancelsChildren() {
var child = Command.noRequirements().executing(Coroutine::park).named("Child 1");
var child = Command.noRequirements(Coroutine::park).named("Child 1");
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.fork(child);
throw new RuntimeException("The exception");
@@ -118,15 +114,13 @@ class SchedulerErrorHandlingTests extends CommandTestBase {
@Test
void childCommandEncounteringErrorCancelsParent() {
var child =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
throw new RuntimeException("The exception"); // note: bubbles up to the parent
})
.named("Child 1");
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.await(child);
co.park(); // pretend other things would happen after the child
@@ -145,16 +139,14 @@ class SchedulerErrorHandlingTests extends CommandTestBase {
@SuppressWarnings("PMD.CompareObjectsWithEquals")
void childCommandEncounteringErrorAfterRemountCancelsParent() {
var child =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.yield();
throw new RuntimeException("The exception"); // does not bubble up to the parent
})
.named("Child 1");
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.await(child);
co.park(); // pretend other things would happen after the child

View File

@@ -41,7 +41,7 @@ class SchedulerSideloadFunctionTests extends CommandTestBase {
@Test
void sideloadSchedulingCommand() {
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
// one-shot sideload forks a command and immediately exits
m_scheduler.sideload(co -> co.fork(command));
m_scheduler.run();
@@ -51,10 +51,9 @@ class SchedulerSideloadFunctionTests extends CommandTestBase {
@Test
void childCommandEscapesViaSideload() {
var child = Command.noRequirements().executing(Coroutine::park).named("Child");
var child = Command.noRequirements(Coroutine::park).named("Child");
var parent =
Command.noRequirements()
.executing(
Command.noRequirements(
parentCoroutine -> {
m_scheduler.sideload(sideloadCoroutine -> sideloadCoroutine.fork(child));
})
@@ -73,7 +72,7 @@ class SchedulerSideloadFunctionTests extends CommandTestBase {
@Test
void sideloadCancelingCommand() {
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
m_scheduler.schedule(command);
m_scheduler.run();
assertTrue(m_scheduler.isRunning(command), "command should have started");
@@ -89,7 +88,7 @@ class SchedulerSideloadFunctionTests extends CommandTestBase {
void sideloadAffectsStateForTriggerInSameCycle() {
AtomicBoolean signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.onTrue(command);
m_scheduler.sideload(co -> signal.set(true));

View File

@@ -20,8 +20,8 @@ class SchedulerTelemetryTests extends CommandTestBase {
m_scheduler.schedule(group);
m_scheduler.run();
var scheduledCommand1 = Command.noRequirements().executing(Coroutine::park).named("Command 1");
var scheduledCommand2 = Command.noRequirements().executing(Coroutine::park).named("Command 2");
var scheduledCommand1 = Command.noRequirements(Coroutine::park).named("Command 1");
var scheduledCommand2 = Command.noRequirements(Coroutine::park).named("Command 2");
m_scheduler.schedule(scheduledCommand1);
m_scheduler.schedule(scheduledCommand2);

View File

@@ -18,8 +18,7 @@ class SchedulerTest extends CommandTestBase {
var enabled = new AtomicBoolean(false);
var ran = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
coroutine -> {
do {
coroutine.yield();
@@ -58,8 +57,7 @@ class SchedulerTest extends CommandTestBase {
for (int cmdCount = 0; cmdCount < numCommands; cmdCount++) {
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
coroutine -> {
for (int i = 0; i < iterations; i++) {
mechanism.m_x++;
@@ -114,8 +112,7 @@ class SchedulerTest extends CommandTestBase {
// the group has no requirements, but can schedule child commands that do
var group =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.awaitAll(
m1.run(Coroutine::park).named("M1 Command"),

View File

@@ -25,8 +25,7 @@ class SchedulerTimingTests extends CommandTestBase {
// equivalent to calling Coroutine.park(). No deleterious side effects other than stalling
// the command
AtomicReference<Command> commandRef = new AtomicReference<>();
var command =
Command.noRequirements().executing(co -> co.await(commandRef.get())).named("Self Await");
var command = Command.noRequirements(co -> co.await(commandRef.get())).named("Self Await");
commandRef.set(command);
m_scheduler.schedule(command);
@@ -49,8 +48,8 @@ class SchedulerTimingTests extends CommandTestBase {
//
// Externally canceling child allows parent to continue
// Externally canceling parent cancels both
var parent = Command.noRequirements().executing(co -> co.await(childRef.get())).named("Parent");
var child = Command.noRequirements().executing(co -> co.await(parentRef.get())).named("Child");
var parent = Command.noRequirements(co -> co.await(childRef.get())).named("Parent");
var child = Command.noRequirements(co -> co.await(parentRef.get())).named("Child");
parentRef.set(parent);
childRef.set(child);
@@ -87,8 +86,7 @@ class SchedulerTimingTests extends CommandTestBase {
//
// Externally canceling either command allows the other to exit
var command1 =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.yield();
co.await(ref2.get());
@@ -96,8 +94,7 @@ class SchedulerTimingTests extends CommandTestBase {
})
.named("Command 1");
var command2 =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.yield();
co.await(ref1.get());
@@ -134,8 +131,7 @@ class SchedulerTimingTests extends CommandTestBase {
AtomicInteger runCount = new AtomicInteger(0);
var inner =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
runCount.incrementAndGet();
co.yield();
@@ -145,7 +141,7 @@ class SchedulerTimingTests extends CommandTestBase {
})
.named("Inner");
var outer = Command.noRequirements().executing(co -> co.await(inner)).named("Outer");
var outer = Command.noRequirements(co -> co.await(inner)).named("Outer");
m_scheduler.schedule(outer);
m_scheduler.run();
@@ -159,8 +155,7 @@ class SchedulerTimingTests extends CommandTestBase {
AtomicBoolean completedWait = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.wait(Milliseconds.of(1));
completedWait.set(true);
@@ -184,8 +179,7 @@ class SchedulerTimingTests extends CommandTestBase {
RobotController.setTimeSource(time::get);
AtomicBoolean completedWait = new AtomicBoolean(false);
var command =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.wait(Milliseconds.of(1));
completedWait.set(true);
@@ -212,8 +206,7 @@ class SchedulerTimingTests extends CommandTestBase {
void awaitingExitsImmediatelyWithoutAOneLoopDelay() {
AtomicInteger innerRuns = new AtomicInteger(0);
var inner =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
// executed immediately when forked
innerRuns.incrementAndGet();
@@ -224,7 +217,7 @@ class SchedulerTimingTests extends CommandTestBase {
})
.named("Inner");
var outer = Command.noRequirements().executing(co -> co.await(inner)).named("Outer");
var outer = Command.noRequirements(co -> co.await(inner)).named("Outer");
m_scheduler.schedule(outer);
// First run: runs outer, forks inner, inner runs to its first yield, outer yields

View File

@@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
class SequentialGroupBuilderTest {
@Test
void andThenSingle() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1).named("Seq");
@@ -25,8 +25,8 @@ class SequentialGroupBuilderTest {
@Test
void andThenMultiple() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c2 = Command.noRequirements().executing(Coroutine::park).named("C2");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var c2 = Command.noRequirements(Coroutine::park).named("C2");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1, c2).named("Seq");
@@ -38,8 +38,8 @@ class SequentialGroupBuilderTest {
@Test
void andThenRepeated() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c2 = Command.noRequirements().executing(Coroutine::park).named("C2");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var c2 = Command.noRequirements(Coroutine::park).named("C2");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1).andThen(c2).named("Seq");
@@ -69,7 +69,7 @@ class SequentialGroupBuilderTest {
@Test
void untilReturnsParallelGroup() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1).until(() -> false).named("Seq");
assertInstanceOf(ParallelGroup.class, sequence);
@@ -91,7 +91,7 @@ class SequentialGroupBuilderTest {
@Test
void automaticNameWithOneCommand() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1).withAutomaticName();
assertEquals("C1", sequence.name());
@@ -99,8 +99,8 @@ class SequentialGroupBuilderTest {
@Test
void automaticNameWithMultipleCommands() {
var c1 = Command.noRequirements().executing(Coroutine::park).named("C1");
var c2 = Command.noRequirements().executing(Coroutine::park).named("C2");
var c1 = Command.noRequirements(Coroutine::park).named("C1");
var c2 = Command.noRequirements(Coroutine::park).named("C2");
var builder = new SequentialGroupBuilder();
var sequence = builder.andThen(c1, c2).withAutomaticName();
assertEquals("C1 -> C2", sequence.name());

View File

@@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
class SequentialGroupTest extends CommandTestBase {
@Test
void single() {
var command = Command.noRequirements().executing(Coroutine::yield).named("The Command");
var command = Command.noRequirements(Coroutine::yield).named("The Command");
var sequence = new SequentialGroup("The Sequence", List.of(command));
m_scheduler.schedule(sequence);
@@ -33,8 +33,8 @@ class SequentialGroupTest extends CommandTestBase {
@Test
void twoCommands() {
var c1 = Command.noRequirements().executing(Coroutine::yield).named("C1");
var c2 = Command.noRequirements().executing(Coroutine::yield).named("C2");
var c1 = Command.noRequirements(Coroutine::yield).named("C1");
var c2 = Command.noRequirements(Coroutine::yield).named("C2");
var sequence = new SequentialGroup("C1 > C2", List.of(c1, c2));
m_scheduler.schedule(sequence);

View File

@@ -17,7 +17,7 @@ class TriggerTest extends CommandTestBase {
void onTrue() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.onTrue(command);
signal.set(true);
@@ -34,7 +34,7 @@ class TriggerTest extends CommandTestBase {
void onFalse() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.onFalse(command);
m_scheduler.run();
@@ -51,7 +51,7 @@ class TriggerTest extends CommandTestBase {
void whileTrue() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.whileTrue(command);
signal.set(true);
@@ -68,7 +68,7 @@ class TriggerTest extends CommandTestBase {
void whileFalse() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.whileFalse(command);
m_scheduler.run();
@@ -84,7 +84,7 @@ class TriggerTest extends CommandTestBase {
void toggleOnTrue() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.toggleOnTrue(command);
m_scheduler.run();
@@ -107,7 +107,7 @@ class TriggerTest extends CommandTestBase {
void toggleOnFalse() {
var signal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, signal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.toggleOnFalse(command);
m_scheduler.run();
@@ -128,8 +128,7 @@ class TriggerTest extends CommandTestBase {
var innerSignal = new AtomicBoolean(false);
var inner =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
while (true) {
innerRan.set(true);
@@ -139,8 +138,7 @@ class TriggerTest extends CommandTestBase {
.named("Inner");
var outer =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
new Trigger(m_scheduler, innerSignal::get).onTrue(inner);
// If we yield, then the outer command exits and immediately cancels the
@@ -170,7 +168,7 @@ class TriggerTest extends CommandTestBase {
var triggerSignal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, triggerSignal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.addBinding(scope, BindingType.RUN_WHILE_HIGH, command);
triggerSignal.set(true);
@@ -209,7 +207,7 @@ class TriggerTest extends CommandTestBase {
var triggerSignal = new AtomicBoolean(false);
var trigger = new Trigger(m_scheduler, triggerSignal::get);
var command = Command.noRequirements().executing(Coroutine::park).named("Command");
var command = Command.noRequirements(Coroutine::park).named("Command");
trigger.whileTrue(command);
triggerSignal.set(true);
@@ -230,8 +228,7 @@ class TriggerTest extends CommandTestBase {
var triggeredCommandRan = new AtomicBoolean(false);
var inner =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
triggeredCommandRan.set(true);
co.park();
@@ -239,8 +236,7 @@ class TriggerTest extends CommandTestBase {
.named("Inner");
var awaited =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
co.yield();
condition.set(true);
@@ -248,8 +244,7 @@ class TriggerTest extends CommandTestBase {
.named("Awaited");
var outer =
Command.noRequirements()
.executing(
Command.noRequirements(
co -> {
new Trigger(m_scheduler, condition::get).onTrue(inner);
co.await(awaited);

View File

@@ -144,7 +144,7 @@ void bindDriveButtons() {
Trigger atScoringPosition = new Trigger(() -> getPosition().isNear(kScoringPosition));
Command autonomous() {
return Command.noRequirements().executing(coroutine -> {
return Command.noRequirements(coroutine -> {
// This binding only exists while the autonomous command is running
atScoringPosition.onTrue(score());
@@ -212,7 +212,7 @@ class ExampleOpmode extends PeriodicOpMode {
robot.gamepad.leftBumper().onTrue(robot.mechanism.run(...).named("Bound Command"));
// A manually scheduled command in an opmode will be canceled when the opmode exits if it hasn't already exited on its own
Scheduler.getInstance().schedule(Command.noRequirements().executing(coroutine -> {
Scheduler.getInstance().schedule(Command.noRequirements(coroutine -> {
// Change the default command while this command is running.
// It will be reset to the opmode-scoped default command when this command exits.
// If no opmode-scoped default command exists, it will be reset to the global default command.
@@ -255,7 +255,7 @@ outside its command makes no sense, and an error will be thrown if attempting to
```java
Coroutine coroutine;
var badCommand = Command.noRequirements().executing(co -> {
var badCommand = Command.noRequirements(co -> {
coroutine = co;
}).named("Do not do this");
@@ -357,13 +357,10 @@ compilation error.
// OK - requirements, body, and name are all provided
// Each builder method returns a different builder object that provides methods
// for progressing to the next stage.
Command command = Command.noRequirements().executing(...).named("Name");
Command command = Command.noRequirements(...).named("Name");
// Compilation error! Missing the command body
Command command = Command.noRequirements().named("Name");
// Compilation error! Missing the command name
Command command = Command.noRequirements().executing(...);
Command command = Command.noRequirements(...);
```
#### Forced Naming