diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java index 40176a1e3e..611730882e 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/Command.java @@ -302,6 +302,18 @@ public interface Command { return new ProxyScheduleCommand(this); } + /** + * Decorates this command to only run if this condition is not met. If the command is already + * running and the condition changes to true, the command will not stop running. The requirements + * of this command will be kept for the new conditonal command. + * + * @param condition the condition that will prevent the command from running + * @return the decorated command + */ + default ConditionalCommand unless(BooleanSupplier condition) { + return new ConditionalCommand(new InstantCommand(), this, condition); + } + /** * Schedules this command. * diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp index 04d315222d..5b58739d0e 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp @@ -5,6 +5,7 @@ #include "frc2/command/Command.h" #include "frc2/command/CommandScheduler.h" +#include "frc2/command/ConditionalCommand.h" #include "frc2/command/EndlessCommand.h" #include "frc2/command/InstantCommand.h" #include "frc2/command/ParallelCommandGroup.h" @@ -101,6 +102,12 @@ ProxyScheduleCommand Command::AsProxy() { return ProxyScheduleCommand(this); } +ConditionalCommand Command::Unless(std::function condition) && { + return ConditionalCommand(std::make_unique(), + std::move(*this).TransferOwnership(), + std::move(condition)); +} + void Command::Schedule(bool interruptible) { CommandScheduler::GetInstance().Schedule(interruptible, this); } diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h index fecff86d8c..8a58e4d1b8 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/Command.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/Command.h @@ -32,6 +32,7 @@ class SequentialCommandGroup; class PerpetualCommand; class ProxyScheduleCommand; class RepeatCommand; +class ConditionalCommand; /** * A state machine representing a complete action to be performed by the robot. @@ -39,8 +40,9 @@ class RepeatCommand; * CommandGroups to allow users to build complicated multi-step actions without * the need to roll the state machine logic themselves. * - *

Commands are run synchronously from the main robot loop; no multithreading - * is used, unless specified explicitly from the command implementation. + *

Commands are run synchronously from the main robot loop; no + * multithreading is used, unless specified explicitly from the command + * implementation. * *

Note: ALWAYS create a subclass by extending CommandHelper, * or decorators will not function! @@ -216,6 +218,17 @@ class Command { */ virtual ProxyScheduleCommand AsProxy(); + /** + * Decorates this command to only run if this condition is not met. If the + * command is already running and the condition changes to true, the command + * will not stop running. The requirements of this command will be kept for + * the new conditonal command. + * + * @param condition the condition that will prevent the command from running + * @return the decorated command + */ + virtual ConditionalCommand Unless(std::function condition) &&; + /** * Schedules this command. * diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java index 66593ca414..b70f8db00e 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/CommandDecoratorTest.java @@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import edu.wpi.first.hal.HAL; import edu.wpi.first.wpilibj.simulation.SimHooks; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.ResourceLock; @@ -182,4 +183,24 @@ class CommandDecoratorTest extends CommandTestBase { assertTrue(scheduler.isScheduled(perpetual)); } } + + @Test + void unlessTest() { + try (CommandScheduler scheduler = new CommandScheduler()) { + AtomicBoolean unlessCondition = new AtomicBoolean(true); + AtomicBoolean hasRunCondition = new AtomicBoolean(false); + + Command command = + new InstantCommand(() -> hasRunCondition.set(true)).unless(unlessCondition::get); + + scheduler.schedule(command); + scheduler.run(); + assertFalse(hasRunCondition.get()); + + unlessCondition.set(false); + scheduler.schedule(command); + scheduler.run(); + assertTrue(hasRunCondition.get()); + } + } } diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp index 4a7c137a47..112f13dae2 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/CommandDecoratorTest.cpp @@ -5,6 +5,7 @@ #include #include "CommandTestBase.h" +#include "frc2/command/ConditionalCommand.h" #include "frc2/command/InstantCommand.h" #include "frc2/command/ParallelRaceGroup.h" #include "frc2/command/PerpetualCommand.h" @@ -101,3 +102,24 @@ TEST_F(CommandDecoratorTest, Perpetually) { EXPECT_TRUE(scheduler.IsScheduled(&command)); } + +TEST_F(CommandDecoratorTest, Unless) { + CommandScheduler scheduler = GetScheduler(); + + bool hasRun = false; + bool unlessBool = true; + + auto command = + InstantCommand([&hasRun] { hasRun = true; }, {}).Unless([&unlessBool] { + return unlessBool; + }); + + scheduler.Schedule(&command); + scheduler.Run(); + EXPECT_FALSE(hasRun); + + unlessBool = false; + scheduler.Schedule(&command); + scheduler.Run(); + EXPECT_TRUE(hasRun); +}