[cmd3] Make Mechanism an interface (#8303)

Since there is no longer a requirement for Subsystems/Mechanisms to be
registered to the command scheduler via a register() call, Mechanism can
be changed to an interface instead to allow for more flexible
inheritance structures.

Specifically, this would allow compatibility with CTRE swerve (which
previously required implementing Subsystem so that it could extend
CTRE's base class).
This commit is contained in:
Daniel Chen
2026-05-18 20:30:12 -04:00
committed by GitHub
parent fa24446ce3
commit e35ca772fd
16 changed files with 123 additions and 116 deletions

View File

@@ -10,61 +10,33 @@ import org.wpilib.annotation.NoDiscard;
import org.wpilib.units.measure.Time;
/**
* Generic base class to represent mechanisms on a robot. Commands can require sole ownership of a
* Generic interface to represent mechanisms on a robot. Commands can require sole ownership of a
* mechanism; when a command that requires a mechanism is running, no other commands may use it at
* the same time.
*
* <p>Even though this class is named "Mechanism", it may be used to represent other physical
* <p>Even though this interface is named "Mechanism", it may be used to represent other physical
* hardware on a robot that should be controlled with commands - for example, an LED strip or a
* vision processor that can switch between different pipelines could be represented as mechanisms.
*/
public class Mechanism {
private final String m_name;
private final Scheduler m_registeredScheduler;
public interface Mechanism {
/**
* Creates a new mechanism registered with the default scheduler instance and named using the name
* of the class. Intended to be used by subclasses to get sane defaults without needing to
* manually declare a constructor.
*/
@SuppressWarnings("this-escape")
protected Mechanism() {
m_name = getClass().getSimpleName();
m_registeredScheduler = Scheduler.getDefault();
setDefaultCommand(idle());
}
/**
* Creates a new mechanism, registered with the default scheduler instance.
* Returns the scheduler under which this subsystem and its default commands are registered. The
* scheduler is also used to fetch running commands for the subsystem.
*
* @param name The name of the mechanism. Cannot be null.
* @return The registered scheduler.
*/
public Mechanism(String name) {
this(name, Scheduler.getDefault());
default Scheduler getRegisteredScheduler() {
return Scheduler.getDefault();
}
/**
* Creates a new mechanism, registered with the given scheduler instance.
*
* @param name The name of the mechanism. Cannot be null.
* @param scheduler The registered scheduler. Cannot be null.
*/
@SuppressWarnings("this-escape")
public Mechanism(String name, Scheduler scheduler) {
m_name = name;
m_registeredScheduler = scheduler;
setDefaultCommand(idle());
}
/**
* Gets the name of this mechanism.
* Gets the name of this mechanism. This will default to the name of this mechanism's class.
*
* @return The name of the mechanism.
*/
@NoDiscard
public String getName() {
return m_name;
default String getName() {
return getClass().getSimpleName();
}
/**
@@ -79,8 +51,8 @@ public class Mechanism {
*
* @param defaultCommand the new default command
*/
public void setDefaultCommand(Command defaultCommand) {
m_registeredScheduler.setDefaultCommand(this, defaultCommand);
default void setDefaultCommand(Command defaultCommand) {
getRegisteredScheduler().setDefaultCommand(this, defaultCommand);
}
/**
@@ -89,8 +61,8 @@ public class Mechanism {
*
* @return The currently configured default command
*/
public Command getDefaultCommand() {
return m_registeredScheduler.getDefaultCommandFor(this);
default Command getDefaultCommand() {
return getRegisteredScheduler().getDefaultCommandFor(this);
}
/**
@@ -99,7 +71,7 @@ public class Mechanism {
* @param commandBody The main function body of the command.
* @return The command builder, for further configuration.
*/
public NeedsNameBuilderStage run(Consumer<Coroutine> commandBody) {
default NeedsNameBuilderStage run(Consumer<Coroutine> commandBody) {
return new StagedCommandBuilder().requiring(this).executing(commandBody);
}
@@ -111,7 +83,7 @@ public class Mechanism {
* @param loopBody The body of the infinite loop.
* @return The command builder, for further configuration.
*/
public NeedsNameBuilderStage runRepeatedly(Runnable loopBody) {
default NeedsNameBuilderStage runRepeatedly(Runnable loopBody) {
return run(
coroutine -> {
while (true) {
@@ -131,7 +103,7 @@ public class Mechanism {
*
* @return A new idle command.
*/
public Command idle() {
default Command idle() {
return run(Coroutine::park).withPriority(Command.LOWEST_PRIORITY).named(getName() + "[IDLE]");
}
@@ -141,7 +113,7 @@ public class Mechanism {
* @param duration How long the mechanism should idle for.
* @return A new idle command.
*/
public Command idleFor(Time duration) {
default Command idleFor(Time duration) {
return idle().withTimeout(duration);
}
@@ -154,12 +126,7 @@ public class Mechanism {
* @return The currently running commands that require the mechanism.
*/
@NoDiscard
public List<Command> getRunningCommands() {
return m_registeredScheduler.getRunningCommandsFor(this);
}
@Override
public String toString() {
return m_name;
default List<Command> getRunningCommands() {
return getRegisteredScheduler().getRunningCommandsFor(this);
}
}

View File

@@ -23,7 +23,7 @@ class ConflictDetectorTest extends CommandTestBase {
@Test
void singleInputHasNoConflicts() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var command = Command.requiring(mech).executing(Coroutine::park).named("Command");
var conflicts = findAllConflicts(List.of(command));
assertEquals(0, conflicts.size());
@@ -31,7 +31,7 @@ class ConflictDetectorTest extends CommandTestBase {
@Test
void commandDoesNotConflictWithSelf() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var command = Command.requiring(mech).executing(Coroutine::park).named("Command");
var conflicts = findAllConflicts(List.of(command, command));
assertEquals(0, conflicts.size());
@@ -39,8 +39,8 @@ class ConflictDetectorTest extends CommandTestBase {
@Test
void detectManyConflicts() {
var mech1 = new Mechanism("Mech 1", m_scheduler);
var mech2 = new Mechanism("Mech 2", m_scheduler);
var mech1 = new DummyMechanism("Mech 1", m_scheduler);
var mech2 = new DummyMechanism("Mech 2", m_scheduler);
var command1 = Command.requiring(mech1, mech2).executing(Coroutine::park).named("Command1");
var command2 = Command.requiring(mech1).executing(Coroutine::park).named("Command2");

View File

@@ -0,0 +1,32 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package org.wpilib.command3;
/** A dummy mechanism that allows inline scheduler and name specification, for use in unit tests. */
class DummyMechanism implements Mechanism {
private final String m_name;
private final Scheduler m_scheduler;
/**
* Creates a dummy mechanism.
*
* @param name The name of this dummy mechanism.
* @param scheduler The registered scheduler. Cannot be null.
*/
DummyMechanism(String name, Scheduler scheduler) {
m_name = name;
m_scheduler = scheduler;
}
@Override
public String getName() {
return m_name;
}
@Override
public Scheduler getRegisteredScheduler() {
return m_scheduler;
}
}

View File

@@ -16,8 +16,8 @@ import org.junit.jupiter.api.Test;
class ParallelGroupTest extends CommandTestBase {
@Test
void parallelAll() {
var r1 = new Mechanism("R1", m_scheduler);
var r2 = new Mechanism("R2", m_scheduler);
var r1 = new DummyMechanism("R1", m_scheduler);
var r2 = new DummyMechanism("R2", m_scheduler);
var c1Count = new AtomicInteger(0);
var c2Count = new AtomicInteger(0);
@@ -81,8 +81,8 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void race() {
var r1 = new Mechanism("R1", m_scheduler);
var r2 = new Mechanism("R2", m_scheduler);
var r1 = new DummyMechanism("R1", m_scheduler);
var r2 = new DummyMechanism("R2", m_scheduler);
var c1Count = new AtomicInteger(0);
var c2Count = new AtomicInteger(0);
@@ -134,7 +134,7 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void nested() {
var mechanism = new Mechanism("mechanism", m_scheduler);
var mechanism = new DummyMechanism("mechanism", m_scheduler);
var count = new AtomicInteger(0);
@@ -213,8 +213,8 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void inheritsRequirements() {
var mech1 = new Mechanism("Mech 1", m_scheduler);
var mech2 = new Mechanism("Mech 2", m_scheduler);
var mech1 = new DummyMechanism("Mech 1", m_scheduler);
var mech2 = new DummyMechanism("Mech 2", m_scheduler);
var command1 = mech1.run(Coroutine::park).named("Command 1");
var command2 = mech2.run(Coroutine::park).named("Command 2");
var group = new ParallelGroup("Group", Set.of(command1, command2), Set.of());
@@ -223,8 +223,8 @@ class ParallelGroupTest extends CommandTestBase {
@Test
void inheritsPriority() {
var mech1 = new Mechanism("Mech 1", m_scheduler);
var mech2 = new Mechanism("Mech 2", m_scheduler);
var mech1 = new DummyMechanism("Mech 1", m_scheduler);
var mech2 = new DummyMechanism("Mech 2", m_scheduler);
var command1 = mech1.run(Coroutine::park).withPriority(100).named("Command 1");
var command2 = mech2.run(Coroutine::park).withPriority(200).named("Command 2");
var group = new ParallelGroup("Group", Set.of(command1, command2), Set.of());

View File

@@ -23,7 +23,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void cancelOnInterruptDoesNotResume() {
var count = new AtomicInteger(0);
var mechanism = new Mechanism("mechanism", m_scheduler);
var mechanism = new DummyMechanism("mechanism", m_scheduler);
var interrupter =
Command.requiring(mechanism)
@@ -54,7 +54,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void defaultCommandResumesAfterInterruption() {
var count = new AtomicInteger(0);
var mechanism = new Mechanism("mechanism", m_scheduler);
var mechanism = new DummyMechanism("mechanism", m_scheduler);
var defaultCmd =
mechanism
.run(
@@ -166,7 +166,9 @@ class SchedulerCancellationTests extends CommandTestBase {
void cancelAllStartsDefaults() {
var mechanisms = new ArrayList<Mechanism>(10);
for (int i = 1; i <= 10; i++) {
mechanisms.add(new Mechanism("System " + i, m_scheduler));
var mechanism = new DummyMechanism("System " + i, m_scheduler);
mechanism.setDefaultCommand(mechanism.idle());
mechanisms.add(mechanism);
}
var command = Command.requiring(mechanisms).executing(Coroutine::yield).named("Big Command");
@@ -235,7 +237,7 @@ class SchedulerCancellationTests extends CommandTestBase {
@Test
void compositionsDoNotSelfCancel() {
var mech = new Mechanism("The mechanism", m_scheduler);
var mech = new DummyMechanism("The mechanism", m_scheduler);
var group =
mech.run(
co -> {
@@ -261,7 +263,7 @@ class SchedulerCancellationTests extends CommandTestBase {
@Test
void compositionsDoNotCancelParent() {
var mech = new Mechanism("The mechanism", m_scheduler);
var mech = new DummyMechanism("The mechanism", m_scheduler);
var group =
mech.run(
co -> {
@@ -284,7 +286,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void doesNotRunOnCancelWhenInterruptingOnDeck() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd");
var interrupter = mechanism.run(Coroutine::yield).named("Interrupter");
m_scheduler.schedule(cmd);
@@ -298,7 +300,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void doesNotRunOnCancelWhenCancelingOnDeck() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd");
m_scheduler.schedule(cmd);
// canceling before calling .run()
@@ -312,7 +314,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void runsOnCancelWhenInterruptingCommand() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::park).whenCanceled(() -> ran.set(true)).named("cmd");
var interrupter = mechanism.run(Coroutine::park).named("Interrupter");
m_scheduler.schedule(cmd);
@@ -327,7 +329,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void doesNotRunOnCancelWhenCompleting() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd");
m_scheduler.schedule(cmd);
m_scheduler.run();
@@ -341,7 +343,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void runsOnCancelWhenCanceling() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd");
m_scheduler.schedule(cmd);
m_scheduler.run();
@@ -354,7 +356,7 @@ class SchedulerCancellationTests extends CommandTestBase {
void runsOnCancelWhenCancelingParent() {
var ran = new AtomicBoolean(false);
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var cmd = mechanism.run(Coroutine::yield).whenCanceled(() -> ran.set(true)).named("cmd");
var group = new SequentialGroup("Seq", Collections.singletonList(cmd));

View File

@@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test;
class SchedulerConflictTests extends CommandTestBase {
@Test
void compositionsCannotAwaitConflictingCommands() {
var mech = new Mechanism("The Mechanism", m_scheduler);
var mech = new DummyMechanism("The Mechanism", m_scheduler);
var group =
Command.noRequirements(
@@ -40,7 +40,7 @@ class SchedulerConflictTests extends CommandTestBase {
@Test
void innerCommandMayInterruptOtherInnerCommand() {
var mechanism = new Mechanism("The mechanism", m_scheduler);
var mechanism = new DummyMechanism("The mechanism", m_scheduler);
var firstRan = new AtomicBoolean(false);
var secondRan = new AtomicBoolean(false);
@@ -118,7 +118,7 @@ class SchedulerConflictTests extends CommandTestBase {
@Test
void childConflictsWithHigherPriorityTopLevel() {
var mechanism = new Mechanism("mechanism", m_scheduler);
var mechanism = new DummyMechanism("mechanism", m_scheduler);
var top = mechanism.run(Coroutine::park).withPriority(10).named("Top");
// Child conflicts with and is lower priority than the Top command
@@ -137,7 +137,7 @@ class SchedulerConflictTests extends CommandTestBase {
@Test
void childConflictsWithLowerPriorityTopLevel() {
var mechanism = new Mechanism("mechanism", m_scheduler);
var mechanism = new DummyMechanism("mechanism", m_scheduler);
var top = mechanism.run(Coroutine::park).withPriority(-10).named("Top");
// Child conflicts with and is higher priority than the Top command

View File

@@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void globalDefaultCommandIsAlwaysUsed() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var defaultCommand = mech.run(Coroutine::park).named("Custom Default Command");
mech.setDefaultCommand(defaultCommand);
@@ -34,7 +34,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void defaultCommandSetInOpmodeStops() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var initialDefaultCommand = mech.idle();
mech.setDefaultCommand(initialDefaultCommand);
@@ -57,7 +57,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void defaultCommandSetInCommandStops() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var initialDefaultCommand = mech.idle();
mech.setDefaultCommand(initialDefaultCommand);
@@ -86,7 +86,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void interruptingDefaultCommandInterruptsOwner() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var initialDefaultCommand = mech.idle();
mech.setDefaultCommand(initialDefaultCommand);
@@ -116,7 +116,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void defaultCommandStackup() {
var mech = new Mechanism("Mech", m_scheduler);
var mech = new DummyMechanism("Mech", m_scheduler);
var initialDefaultCommand = mech.idle();
mech.setDefaultCommand(initialDefaultCommand);
@@ -168,7 +168,7 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
@Test
void defaultCommandChangingDefaultCommand() {
var mech =
new Mechanism("Mech", m_scheduler) {
new DummyMechanism("Mech", m_scheduler) {
Command makeCommand1() {
return run(co -> {
setDefaultCommand(makeCommand2());
@@ -204,12 +204,12 @@ class SchedulerDefaultCommandTests extends CommandTestBase {
// we'd have 1 binding object per loop and quickly use a ton of memory
var defaultCommandBindings = m_scheduler.getDefaultCommandBindingsFor(mech);
assertEquals(
List.of("Mech[IDLE]", "Command 1", "Command 2", "Command 1"),
List.of("Command 1", "Command 2", "Command 1"),
defaultCommandBindings.stream().map(b -> b.command().name()).toList());
m_scheduler.run();
assertEquals(
List.of("Mech[IDLE]", "Command 1", "Command 2"),
List.of("Command 1", "Command 2"),
defaultCommandBindings.stream().map(b -> b.command().name()).toList());
}
}

View File

@@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
class SchedulerErrorHandlingTests extends CommandTestBase {
@Test
void errorDetection() {
var mechanism = new Mechanism("X", m_scheduler);
var mechanism = new DummyMechanism("X", m_scheduler);
var command =
mechanism

View File

@@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test;
class SchedulerPriorityLevelTests extends CommandTestBase {
@Test
void higherPriorityCancels() {
final var subsystem = new Mechanism("Subsystem", m_scheduler);
final var subsystem = new DummyMechanism("Subsystem", m_scheduler);
final var lower = new PriorityCommand(-1000, subsystem);
final var higher = new PriorityCommand(+1000, subsystem);
@@ -29,7 +29,7 @@ class SchedulerPriorityLevelTests extends CommandTestBase {
@Test
void lowerPriorityDoesNotCancel() {
final var subsystem = new Mechanism("Subsystem", m_scheduler);
final var subsystem = new DummyMechanism("Subsystem", m_scheduler);
final var lower = new PriorityCommand(-1000, subsystem);
final var higher = new PriorityCommand(+1000, subsystem);
@@ -47,7 +47,7 @@ class SchedulerPriorityLevelTests extends CommandTestBase {
@Test
void samePriorityCancels() {
final var subsystem = new Mechanism("Subsystem", m_scheduler);
final var subsystem = new DummyMechanism("Subsystem", m_scheduler);
final var first = new PriorityCommand(512, subsystem);
final var second = new PriorityCommand(512, subsystem);

View File

@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test;
class SchedulerTelemetryTests extends CommandTestBase {
@Test
void protobuf() {
var mech = new Mechanism("The mechanism", m_scheduler);
var mech = new DummyMechanism("The mechanism", m_scheduler);
var parkCommand = mech.run(Coroutine::park).named("Park");
var c3Command = mech.run(co -> co.await(parkCommand)).named("C3");
var c2Command = mech.run(co -> co.await(c3Command)).named("C2");

View File

@@ -44,7 +44,7 @@ class SchedulerTest extends CommandTestBase {
@SuppressWarnings("PMD.ImmutableField") // PMD bugs
void atomicity() {
var mechanism =
new Mechanism("X", m_scheduler) {
new DummyMechanism("X", m_scheduler) {
int m_x = 0;
};
@@ -80,7 +80,7 @@ class SchedulerTest extends CommandTestBase {
@SuppressWarnings("PMD.ImmutableField") // PMD bugs
void runMechanism() {
var example =
new Mechanism("Counting", m_scheduler) {
new DummyMechanism("Counting", m_scheduler) {
int m_x = 0;
};
@@ -107,8 +107,8 @@ class SchedulerTest extends CommandTestBase {
@Test
void compositionsDoNotNeedRequirements() {
var m1 = new Mechanism("M1", m_scheduler);
var m2 = new Mechanism("m2", m_scheduler);
var m1 = new DummyMechanism("M1", m_scheduler);
var m2 = new DummyMechanism("m2", m_scheduler);
// the group has no requirements, but can schedule child commands that do
var group =
@@ -128,9 +128,15 @@ class SchedulerTest extends CommandTestBase {
@Test
void nestedMechanisms() {
var superstructure =
new Mechanism("Superstructure", m_scheduler) {
private final Mechanism m_elevator = new Mechanism("Elevator", m_scheduler);
private final Mechanism m_arm = new Mechanism("Arm", m_scheduler);
new DummyMechanism("Superstructure", m_scheduler) {
private final Mechanism m_elevator = new DummyMechanism("Elevator", m_scheduler);
private final Mechanism m_arm = new DummyMechanism("Arm", m_scheduler);
{
m_arm.setDefaultCommand(m_arm.idle());
m_elevator.setDefaultCommand(m_elevator.idle());
setDefaultCommand(this.idle());
}
public Command superCommand() {
return run(co -> {

View File

@@ -62,8 +62,8 @@ class SequentialGroupTest extends CommandTestBase {
@Test
void inheritsRequirements() {
var mech1 = new Mechanism("Mech 1", m_scheduler);
var mech2 = new Mechanism("Mech 2", m_scheduler);
var mech1 = new DummyMechanism("Mech 1", m_scheduler);
var mech2 = new DummyMechanism("Mech 2", m_scheduler);
var command1 = mech1.run(Coroutine::park).named("Command 1");
var command2 = mech2.run(Coroutine::park).named("Command 2");
var sequence = new SequentialGroup("Sequence", List.of(command1, command2));
@@ -72,8 +72,8 @@ class SequentialGroupTest extends CommandTestBase {
@Test
void inheritsPriority() {
var mech1 = new Mechanism("Mech 1", m_scheduler);
var mech2 = new Mechanism("Mech 2", m_scheduler);
var mech1 = new DummyMechanism("Mech 1", m_scheduler);
var mech2 = new DummyMechanism("Mech 2", m_scheduler);
var command1 = mech1.run(Coroutine::park).withPriority(100).named("Command 1");
var command2 = mech2.run(Coroutine::park).withPriority(200).named("Command 2");
var sequence = new SequentialGroup("Sequence", List.of(command1, command2));

View File

@@ -24,8 +24,8 @@ class StagedCommandBuilderTest {
@BeforeEach
void setUp() {
Scheduler scheduler = Scheduler.createIndependentScheduler();
m_mech1 = new Mechanism("Mech 1", scheduler);
m_mech2 = new Mechanism("Mech 2", scheduler);
m_mech1 = new DummyMechanism("Mech 1", scheduler);
m_mech2 = new DummyMechanism("Mech 2", scheduler);
}
// The next two tests are to check that various forms of builder usage are able to compile.
@@ -44,7 +44,7 @@ class StagedCommandBuilderTest {
@Test
void allOptions() {
var mech = new Mechanism("Mech", Scheduler.createIndependentScheduler());
var mech = new DummyMechanism("Mech", Scheduler.createIndependentScheduler());
Command command =
new StagedCommandBuilder()

View File

@@ -26,7 +26,7 @@ class StateMachineTest extends CommandTestBase {
@Test
@SuppressWarnings(PostConstructionInitializer.SUPPRESSION_KEY)
void errorsWithoutInitialState() {
Mechanism mech = new Mechanism("Mechanism", m_scheduler);
Mechanism mech = new DummyMechanism("Mechanism", m_scheduler);
Command command1 = mech.run(Coroutine::park).named("Command 1");
Command command2 = mech.run(Coroutine::park).named("Command 2");
@@ -49,7 +49,7 @@ class StateMachineTest extends CommandTestBase {
@Test
void initialStateCanBeOverridden() {
Mechanism mech = new Mechanism("Mechanism", m_scheduler);
Mechanism mech = new DummyMechanism("Mechanism", m_scheduler);
Command command1 = mech.run(Coroutine::park).named("Command 1");
Command command2 = mech.run(Coroutine::park).named("Command 2");
@@ -72,7 +72,7 @@ class StateMachineTest extends CommandTestBase {
AtomicBoolean signalA = new AtomicBoolean(false);
AtomicBoolean signalB = new AtomicBoolean(false);
Mechanism mech = new Mechanism("Mechanism", m_scheduler);
Mechanism mech = new DummyMechanism("Mechanism", m_scheduler);
var command1 = mech.run(Coroutine::park).named("Command 1");
var command2 = mech.run(Coroutine::park).named("Command 2");
var command3 = mech.run(Coroutine::park).named("Command 3");
@@ -565,7 +565,7 @@ class StateMachineTest extends CommandTestBase {
@Test
void onExitCanSchedule() {
var mech = new Mechanism("Mechanism", m_scheduler);
var mech = new DummyMechanism("Mechanism", m_scheduler);
var mainMechCommand = mech.run(Coroutine::park).named("Main Mech Command");
var backgroundMechCommand = mech.run(Coroutine::park).named("Background Mech Command");
var nextStateCommand = Command.noRequirements(Coroutine::park).named("Next");
@@ -697,7 +697,7 @@ class StateMachineTest extends CommandTestBase {
@Test
void ledStateMachine() {
var leds =
new Mechanism("LEDs", m_scheduler) {
new DummyMechanism("LEDs", m_scheduler) {
Command idleAnimation() {
return run(Coroutine::park).withPriority(-1).named("Default Animation");
}

View File

@@ -11,7 +11,7 @@ import org.wpilib.hardware.motor.PWMSparkMax;
import org.wpilib.hardware.rotation.Encoder;
import org.wpilib.util.sendable.SendableRegistry;
public class DriveSubsystem extends Mechanism {
public class DriveSubsystem implements Mechanism {
// The motors on the left side of the drive.
private final PWMSparkMax leftLeader = new PWMSparkMax(DriveConstants.kLeftMotor1Port);
private final PWMSparkMax leftFollower = new PWMSparkMax(DriveConstants.kLeftMotor2Port);

View File

@@ -14,7 +14,7 @@ import org.wpilib.hardware.pneumatic.DoubleSolenoid;
import org.wpilib.hardware.pneumatic.PneumaticsModuleType;
/** A hatch mechanism actuated by a single {@link org.wpilib.hardware.pneumatic.DoubleSolenoid}. */
public class HatchSubsystem extends Mechanism {
public class HatchSubsystem implements Mechanism {
private final DoubleSolenoid hatchSolenoid =
new DoubleSolenoid(
0,