From 2e58ff9f6f9b73af58978a1bf30dc0f0d89e32a3 Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Fri, 22 Nov 2024 19:01:30 -0800 Subject: [PATCH 1/7] Allow specifying initial condition of Trigger bindings --- .../wpilibj2/command/button/Trigger.java | 142 +++++++++++++++-- .../cpp/frc2/command/button/Trigger.cpp | 144 ++++++++++-------- .../include/frc2/command/button/Trigger.h | 84 ++++++++-- .../wpilibj2/command/button/TriggerTest.java | 88 +++++++++++ .../cpp/frc2/command/button/TriggerTest.cpp | 85 +++++++++++ 5 files changed, 448 insertions(+), 95 deletions(-) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index d7b1dd08937..cc2d38d2aaa 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -24,6 +24,20 @@ *

This class is provided by the NewCommands VendorDep */ public class Trigger implements BooleanSupplier { + /** + * Enum specifying the initial state to use for a binding. This impacts whether or not the binding will be triggered immediately. + */ + public enum InitialState { + /** Indicates the binding should assume the condition starts as false. */ + FALSE, + /** Indicates the binding should assume the condition starts as true. */ + TRUE, + /** Indicates the binding should use the trigger's condition. */ + CONDITION, + /** Indicates the binding should use the negated trigger's condition. */ + NEG_CONDITION; + } + private final BooleanSupplier m_condition; private final EventLoop m_loop; @@ -50,16 +64,42 @@ public Trigger(BooleanSupplier condition) { } /** - * Starts the command when the condition changes. + * Gets the initial state for a binding based on an initial state policy. + * + * @param initialState Initial state policy. + * @return The initial state to use. + */ + private boolean getInitialState(InitialState initialState) { + return switch (initialState) { + case FALSE -> false; + case TRUE -> true; + case CONDITION -> m_condition.getAsBoolean(); + case NEG_CONDITION -> !m_condition.getAsBoolean(); + }; + } + + /** + * Starts the command when the condition changes. The command is never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onChange(Command command) { + return onChange(command, InitialState.CONDITION); + } + + /** + * Starts the command when the condition changes. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onChange(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onChange"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -76,16 +116,27 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `false` to `true`. + * Starts the given command whenever the condition changes from `false` to `true`. The command is never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onTrue(Command command) { + return onTrue(command, InitialState.CONDITION); + } + + /** + * Starts the given command whenever the condition changes from `false` to `true`. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onTrue(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onTrue"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -102,16 +153,27 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `true` to `false`. + * Starts the given command whenever the condition changes from `true` to `false`. The command is never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained */ public Trigger onFalse(Command command) { + return onFalse(command, InitialState.CONDITION); + } + + /** + * Starts the given command whenever the condition changes from `true` to `false`. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger onFalse(Command command, InitialState initialState) { requireNonNullParam(command, "command", "onFalse"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -129,7 +191,7 @@ public void run() { /** * Starts the given command when the condition changes to `true` and cancels it when the condition - * changes to `false`. + * changes to `false`. The command is never started immediately. * *

Doesn't re-start the command if it ends while the condition is still `true`. If the command * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. @@ -138,10 +200,25 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileTrue(Command command) { + return whileTrue(command, InitialState.CONDITION); + } + + /** + * Starts the given command when the condition changes to `true` and cancels it when the condition + * changes to `false`. + * + *

Doesn't re-start the command if it ends while the condition is still `true`. If the command + * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger whileTrue(Command command, InitialState initialState) { requireNonNullParam(command, "command", "whileTrue"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -161,7 +238,7 @@ public void run() { /** * Starts the given command when the condition changes to `false` and cancels it when the - * condition changes to `true`. + * condition changes to `true`. The command is never started immediately. * *

Doesn't re-start the command if it ends while the condition is still `false`. If the command * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. @@ -170,10 +247,25 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileFalse(Command command) { + return whileFalse(command, InitialState.CONDITION); + } + + /** + * Starts the given command when the condition changes to `false` and cancels it when the + * condition changes to `true`. + * + *

Doesn't re-start the command if it ends while the condition is still `false`. If the command + * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. + * + * @param command the command to start + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger whileFalse(Command command, InitialState initialState) { requireNonNullParam(command, "command", "whileFalse"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -192,16 +284,27 @@ public void run() { } /** - * Toggles a command when the condition changes from `false` to `true`. + * Toggles a command when the condition changes from `false` to `true`. The command is never toggled immediately. * * @param command the command to toggle * @return this trigger, so calls can be chained */ public Trigger toggleOnTrue(Command command) { + return toggleOnTrue(command, InitialState.CONDITION); + } + + /** + * Toggles a command when the condition changes from `false` to `true`. + * + * @param command the command to toggle + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger toggleOnTrue(Command command, InitialState initialState) { requireNonNullParam(command, "command", "toggleOnTrue"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { @@ -222,16 +325,27 @@ public void run() { } /** - * Toggles a command when the condition changes from `true` to `false`. + * Toggles a command when the condition changes from `true` to `false`. The command is never toggled immediately. * * @param command the command to toggle * @return this trigger, so calls can be chained */ public Trigger toggleOnFalse(Command command) { + return toggleOnFalse(command, InitialState.CONDITION); + } + + /** + * Toggles a command when the condition changes from `true` to `false`. + * + * @param command the command to toggle + * @param initialState the initial state to use + * @return this trigger, so calls can be chained + */ + public Trigger toggleOnFalse(Command command, InitialState initialState) { requireNonNullParam(command, "command", "toggleOnFalse"); m_loop.bind( new Runnable() { - private boolean m_pressedLast = m_condition.getAsBoolean(); + private boolean m_pressedLast = getInitialState(initialState); @Override public void run() { diff --git a/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp b/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp index a3b02d18f3a..420ff488d19 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp @@ -15,22 +15,23 @@ using namespace frc2; Trigger::Trigger(const Trigger& other) = default; -Trigger Trigger::OnChange(Command* command) { - m_loop->Bind( - [condition = m_condition, previous = m_condition(), command]() mutable { - bool current = condition(); +Trigger Trigger::OnChange(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command]() mutable { + bool current = condition(); - if (previous != current) { - command->Schedule(); - } + if (previous != current) { + command->Schedule(); + } - previous = current; - }); + previous = current; + }); return *this; } -Trigger Trigger::OnChange(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::OnChange(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -43,22 +44,23 @@ Trigger Trigger::OnChange(CommandPtr&& command) { return *this; } -Trigger Trigger::OnTrue(Command* command) { - m_loop->Bind( - [condition = m_condition, previous = m_condition(), command]() mutable { - bool current = condition(); +Trigger Trigger::OnTrue(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command]() mutable { + bool current = condition(); - if (!previous && current) { - command->Schedule(); - } + if (!previous && current) { + command->Schedule(); + } - previous = current; - }); + previous = current; + }); return *this; } -Trigger Trigger::OnTrue(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::OnTrue(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -71,22 +73,23 @@ Trigger Trigger::OnTrue(CommandPtr&& command) { return *this; } -Trigger Trigger::OnFalse(Command* command) { - m_loop->Bind( - [condition = m_condition, previous = m_condition(), command]() mutable { - bool current = condition(); +Trigger Trigger::OnFalse(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command]() mutable { + bool current = condition(); - if (previous && !current) { - command->Schedule(); - } + if (previous && !current) { + command->Schedule(); + } - previous = current; - }); + previous = current; + }); return *this; } -Trigger Trigger::OnFalse(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::OnFalse(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -99,24 +102,25 @@ Trigger Trigger::OnFalse(CommandPtr&& command) { return *this; } -Trigger Trigger::WhileTrue(Command* command) { - m_loop->Bind( - [condition = m_condition, previous = m_condition(), command]() mutable { - bool current = condition(); +Trigger Trigger::WhileTrue(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command]() mutable { + bool current = condition(); - if (!previous && current) { - command->Schedule(); - } else if (previous && !current) { - command->Cancel(); - } + if (!previous && current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } - previous = current; - }); + previous = current; + }); return *this; } -Trigger Trigger::WhileTrue(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::WhileTrue(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -131,24 +135,25 @@ Trigger Trigger::WhileTrue(CommandPtr&& command) { return *this; } -Trigger Trigger::WhileFalse(Command* command) { - m_loop->Bind( - [condition = m_condition, previous = m_condition(), command]() mutable { - bool current = condition(); +Trigger Trigger::WhileFalse(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command]() mutable { + bool current = condition(); - if (previous && !current) { - command->Schedule(); - } else if (!previous && current) { - command->Cancel(); - } + if (previous && !current) { + command->Schedule(); + } else if (!previous && current) { + command->Cancel(); + } - previous = current; - }); + previous = current; + }); return *this; } -Trigger Trigger::WhileFalse(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::WhileFalse(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -163,8 +168,9 @@ Trigger Trigger::WhileFalse(CommandPtr&& command) { return *this; } -Trigger Trigger::ToggleOnTrue(Command* command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::ToggleOnTrue(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = command]() mutable { bool current = condition(); @@ -181,8 +187,9 @@ Trigger Trigger::ToggleOnTrue(Command* command) { return *this; } -Trigger Trigger::ToggleOnTrue(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::ToggleOnTrue(CommandPtr&& command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); @@ -199,8 +206,9 @@ Trigger Trigger::ToggleOnTrue(CommandPtr&& command) { return *this; } -Trigger Trigger::ToggleOnFalse(Command* command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::ToggleOnFalse(Command* command, InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = command]() mutable { bool current = condition(); @@ -217,8 +225,10 @@ Trigger Trigger::ToggleOnFalse(Command* command) { return *this; } -Trigger Trigger::ToggleOnFalse(CommandPtr&& command) { - m_loop->Bind([condition = m_condition, previous = m_condition(), +Trigger Trigger::ToggleOnFalse(CommandPtr&& command, + InitialState initialState) { + m_loop->Bind([condition = m_condition, + previous = GetInitialState(initialState), command = std::move(command)]() mutable { bool current = condition(); diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index a2382631334..6b6b4b3c57f 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -30,6 +30,21 @@ class Command; */ class Trigger { public: + /** + * Enum specifying the initial state to use for a binding. This impacts + * whether or not the binding will be triggered immediately. + */ + enum struct InitialState { + /** Indicates the binding should assume the condition starts as false. */ + FALSE, + /** Indicates the binding should assume the condition starts as true. */ + TRUE, + /** Indicates the binding should use the trigger's condition. */ + CONDITION, + /** Indicates the binding should use the negated trigger's condition. */ + NEG_CONDITION + }; + /** * Creates a new trigger based on the given condition. * @@ -61,18 +76,22 @@ class Trigger { * Starts the command when the condition changes. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger OnChange(Command* command); + Trigger OnChange(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Starts the command when the condition changes. Moves command ownership to * the button scheduler. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger OnChange(CommandPtr&& command); + Trigger OnChange(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command whenever the condition changes from `false` to @@ -82,18 +101,22 @@ class Trigger { * lifespan of the command. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger OnTrue(Command* command); + Trigger OnTrue(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command whenever the condition changes from `false` to * `true`. Moves command ownership to the button scheduler. * * @param command The command to bind. + * @param initialState the initial state to use * @return The trigger, for chained calls. */ - Trigger OnTrue(CommandPtr&& command); + Trigger OnTrue(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command whenever the condition changes from `true` to @@ -103,18 +126,22 @@ class Trigger { * lifespan of the command. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger OnFalse(Command* command); + Trigger OnFalse(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command whenever the condition changes from `true` to * `false`. * * @param command The command to bind. + * @param initialState the initial state to use * @return The trigger, for chained calls. */ - Trigger OnFalse(CommandPtr&& command); + Trigger OnFalse(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command when the condition changes to `true` and cancels @@ -127,9 +154,11 @@ class Trigger { * lifespan of the command. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger WhileTrue(Command* command); + Trigger WhileTrue(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command when the condition changes to `true` and cancels @@ -140,9 +169,11 @@ class Trigger { * `true`. If the command should restart, see RepeatCommand. * * @param command The command to bind. + * @param initialState the initial state to use * @return The trigger, for chained calls. */ - Trigger WhileTrue(CommandPtr&& command); + Trigger WhileTrue(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command when the condition changes to `false` and cancels @@ -155,9 +186,11 @@ class Trigger { * lifespan of the command. * * @param command the command to start + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger WhileFalse(Command* command); + Trigger WhileFalse(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Starts the given command when the condition changes to `false` and cancels @@ -168,9 +201,11 @@ class Trigger { * `false`. If the command should restart, see RepeatCommand. * * @param command The command to bind. + * @param initialState the initial state to use * @return The trigger, for chained calls. */ - Trigger WhileFalse(CommandPtr&& command); + Trigger WhileFalse(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Toggles a command when the condition changes from `false` to `true`. @@ -179,9 +214,11 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger ToggleOnTrue(Command* command); + Trigger ToggleOnTrue(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Toggles a command when the condition changes from `false` to `true`. @@ -190,9 +227,11 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger ToggleOnTrue(CommandPtr&& command); + Trigger ToggleOnTrue(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Toggles a command when the condition changes from `true` to the low @@ -202,9 +241,11 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger ToggleOnFalse(Command* command); + Trigger ToggleOnFalse(Command* command, + InitialState initialState = InitialState::CONDITION); /** * Toggles a command when the condition changes from `true` to `false`. @@ -213,9 +254,11 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle + * @param initialState the initial state to use * @return this trigger, so calls can be chained */ - Trigger ToggleOnFalse(CommandPtr&& command); + Trigger ToggleOnFalse(CommandPtr&& command, + InitialState initialState = InitialState::CONDITION); /** * Composes two triggers with logical AND. @@ -291,6 +334,19 @@ class Trigger { bool Get() const; private: + bool GetInitialState(InitialState initialState) const { + switch (initialState) { + case InitialState::FALSE: + return false; + case InitialState::TRUE: + return true; + case InitialState::CONDITION: + return m_condition(); + case InitialState::NEG_CONDITION: + return !m_condition(); + } + } + frc::EventLoop* m_loop; std::function m_condition; }; diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java index 8cd5fdea2f1..b2722cabf54 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java @@ -7,21 +7,29 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import edu.wpi.first.wpilibj.simulation.SimHooks; import edu.wpi.first.wpilibj2.command.Command; +import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.CommandScheduler; import edu.wpi.first.wpilibj2.command.CommandTestBase; import edu.wpi.first.wpilibj2.command.FunctionalCommand; import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.StartEndCommand; import edu.wpi.first.wpilibj2.command.WaitUntilCommand; +import edu.wpi.first.wpilibj2.command.button.Trigger.InitialState; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.Arguments; class TriggerTest extends CommandTestBase { @Test @@ -43,6 +51,23 @@ void onTrueTest() { assertFalse(command1.isScheduled()); } + @ParameterizedTest + @MethodSource("initialStates") + void onTrueInitialStateTest(InitialState initialState, boolean pressed, Map results) { + CommandScheduler scheduler = CommandScheduler.getInstance(); + Command command1 = Commands.idle(); + InternalButton button = new InternalButton(); + boolean shouldBeScheduled = results.get("onTrue"); + + button.setPressed(pressed); + button.onTrue(command1, initialState); + + assertFalse(command1.isScheduled()); + + scheduler.run(); + assertEquals(shouldBeScheduled, command1.isScheduled()); + } + @Test void onFalseTest() { CommandScheduler scheduler = CommandScheduler.getInstance(); @@ -62,6 +87,23 @@ void onFalseTest() { assertFalse(command1.isScheduled()); } + @ParameterizedTest + @MethodSource("initialStates") + void onFalseInitialStateTest(InitialState initialState, boolean pressed, Map results) { + CommandScheduler scheduler = CommandScheduler.getInstance(); + Command command1 = Commands.idle(); + InternalButton button = new InternalButton(); + boolean shouldBeScheduled = results.get("onFalse"); + + button.setPressed(pressed); + button.onFalse(command1, initialState); + + assertFalse(command1.isScheduled()); + + scheduler.run(); + assertEquals(shouldBeScheduled, command1.isScheduled()); + } + @Test void onChangeTest() { CommandScheduler scheduler = CommandScheduler.getInstance(); @@ -88,6 +130,23 @@ void onChangeTest() { assertFalse(command1.isScheduled()); } + @ParameterizedTest + @MethodSource("initialStates") + void onChangeInitialStateTest(InitialState initialState, boolean pressed, Map results) { + CommandScheduler scheduler = CommandScheduler.getInstance(); + Command command1 = Commands.idle(); + InternalButton button = new InternalButton(); + boolean shouldBeScheduled = results.get("onTrue") || results.get("onFalse"); + + button.setPressed(pressed); + button.onChange(command1, initialState); + + assertFalse(command1.isScheduled()); + + scheduler.run(); + assertEquals(shouldBeScheduled, command1.isScheduled()); + } + @Test void whileTrueRepeatedlyTest() { CommandScheduler scheduler = CommandScheduler.getInstance(); @@ -195,6 +254,23 @@ void toggleOnTrueTest() { assertEquals(1, endCounter.get()); } + @ParameterizedTest + @MethodSource("initialStates") + void toggleOnTrueInitialStateTest(InitialState initialState, boolean pressed, Map results) { + CommandScheduler scheduler = CommandScheduler.getInstance(); + Command command1 = Commands.idle(); + InternalButton button = new InternalButton(); + boolean shouldBeScheduled = results.get("onTrue"); + + button.setPressed(pressed); + button.onTrue(command1, initialState); + + assertFalse(command1.isScheduled()); + + scheduler.run(); + assertEquals(shouldBeScheduled, command1.isScheduled()); + } + @Test void cancelWhenActiveTest() { CommandScheduler scheduler = CommandScheduler.getInstance(); @@ -275,4 +351,16 @@ void booleanSupplierTest() { button.setPressed(true); assertTrue(button.getAsBoolean()); } + + static Stream initialStates() { + return Stream.of( + arguments(InitialState.FALSE, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.FALSE, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.TRUE, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.TRUE, false, Map.of("onTrue", false, "onFalse", true)), + arguments(InitialState.CONDITION, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.CONDITION, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.NEG_CONDITION, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.NEG_CONDITION, false, Map.of("onTrue", false, "onFalse", true))); + } } diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp index 9b0899edb78..b857d62d374 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp @@ -18,6 +18,14 @@ using namespace frc2; class TriggerTest : public CommandTestBase {}; +// Initial state, pressed, rising, falling +using InitialStateTestData = + std::tuple; + +class TriggerInitialStateTest + : public TriggerTest, + public testing::WithParamInterface {}; + TEST_F(TriggerTest, OnTrue) { auto& scheduler = CommandScheduler::GetInstance(); bool finished = false; @@ -36,6 +44,22 @@ TEST_F(TriggerTest, OnTrue) { EXPECT_FALSE(scheduler.IsScheduled(&command)); } +TEST_P(TriggerInitialStateTest, OnTrue) { + auto [initialState, pressed, rising, falling] = GetParam(); + auto& scheduler = CommandScheduler::GetInstance(); + RunCommand command([] {}); + bool shouldBeScheduled = rising; + + Trigger([pressed = pressed] { + return pressed; + }).OnTrue(&command, initialState); + + EXPECT_FALSE(scheduler.IsScheduled(&command)); + + scheduler.Run(); + EXPECT_EQ(shouldBeScheduled, scheduler.IsScheduled(&command)); +} + TEST_F(TriggerTest, OnFalse) { auto& scheduler = CommandScheduler::GetInstance(); bool finished = false; @@ -54,6 +78,22 @@ TEST_F(TriggerTest, OnFalse) { EXPECT_FALSE(scheduler.IsScheduled(&command)); } +TEST_P(TriggerInitialStateTest, OnFalse) { + auto [initialState, pressed, rising, falling] = GetParam(); + auto& scheduler = CommandScheduler::GetInstance(); + RunCommand command([] {}); + bool shouldBeScheduled = falling; + + Trigger([pressed = pressed] { + return pressed; + }).OnFalse(&command, initialState); + + EXPECT_FALSE(scheduler.IsScheduled(&command)); + + scheduler.Run(); + EXPECT_EQ(shouldBeScheduled, scheduler.IsScheduled(&command)); +} + TEST_F(TriggerTest, OnChange) { auto& scheduler = CommandScheduler::GetInstance(); bool finished = false; @@ -78,6 +118,22 @@ TEST_F(TriggerTest, OnChange) { EXPECT_FALSE(command.IsScheduled()); } +TEST_P(TriggerInitialStateTest, OnChange) { + auto [initialState, pressed, rising, falling] = GetParam(); + auto& scheduler = CommandScheduler::GetInstance(); + RunCommand command([] {}); + bool shouldBeScheduled = rising || falling; + + Trigger([pressed = pressed] { + return pressed; + }).OnChange(&command, initialState); + + EXPECT_FALSE(scheduler.IsScheduled(&command)); + + scheduler.Run(); + EXPECT_EQ(shouldBeScheduled, scheduler.IsScheduled(&command)); +} + TEST_F(TriggerTest, WhileTrueRepeatedly) { auto& scheduler = CommandScheduler::GetInstance(); int inits = 0; @@ -175,6 +231,22 @@ TEST_F(TriggerTest, ToggleOnTrue) { EXPECT_EQ(1, endCounter); } +TEST_P(TriggerInitialStateTest, ToggleOnTrue) { + auto [initialState, pressed, rising, falling] = GetParam(); + auto& scheduler = CommandScheduler::GetInstance(); + RunCommand command([] {}); + bool shouldBeScheduled = rising; + + Trigger([pressed = pressed] { + return pressed; + }).ToggleOnTrue(&command, initialState); + + EXPECT_FALSE(scheduler.IsScheduled(&command)); + + scheduler.Run(); + EXPECT_EQ(shouldBeScheduled, scheduler.IsScheduled(&command)); +} + TEST_F(TriggerTest, And) { auto& scheduler = CommandScheduler::GetInstance(); bool finished = false; @@ -247,3 +319,16 @@ TEST_F(TriggerTest, Debounce) { scheduler.Run(); EXPECT_TRUE(scheduler.IsScheduled(&command)); } + +INSTANTIATE_TEST_SUITE_P( + InitialState, TriggerInitialStateTest, + testing::Values( + // Initial state, pressed, rising, falling + std::tuple{Trigger::InitialState::FALSE, false, false, false}, + std::tuple{Trigger::InitialState::FALSE, true, true, false}, + std::tuple{Trigger::InitialState::TRUE, false, false, true}, + std::tuple{Trigger::InitialState::TRUE, true, false, false}, + std::tuple{Trigger::InitialState::CONDITION, false, false, false}, + std::tuple{Trigger::InitialState::CONDITION, true, false, false}, + std::tuple{Trigger::InitialState::NEG_CONDITION, false, false, true}, + std::tuple{Trigger::InitialState::NEG_CONDITION, true, true, false})); From 046750efd93cd20bec511bf324751267bf74dd7e Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Fri, 22 Nov 2024 19:10:16 -0800 Subject: [PATCH 2/7] Add default case to C++ --- .../src/main/native/include/frc2/command/button/Trigger.h | 1 + 1 file changed, 1 insertion(+) diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index 6b6b4b3c57f..ddc7b67932c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -345,6 +345,7 @@ class Trigger { case InitialState::NEG_CONDITION: return !m_condition(); } + return false; } frc::EventLoop* m_loop; From eb5c0aded1dbb49adf98721e55435f29b012f52a Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Fri, 22 Nov 2024 19:21:19 -0800 Subject: [PATCH 3/7] IWYU, rename gtest instantiation --- .../src/test/native/cpp/frc2/command/button/TriggerTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp index b857d62d374..c4008fc7630 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp @@ -2,6 +2,7 @@ // 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. +#include #include #include @@ -321,7 +322,7 @@ TEST_F(TriggerTest, Debounce) { } INSTANTIATE_TEST_SUITE_P( - InitialState, TriggerInitialStateTest, + InitialStateTests, TriggerInitialStateTest, testing::Values( // Initial state, pressed, rising, falling std::tuple{Trigger::InitialState::FALSE, false, false, false}, From 1045e076fe7801e30d292e12b6939aa656605fdf Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Fri, 22 Nov 2024 19:28:04 -0800 Subject: [PATCH 4/7] Java format --- .../wpilibj2/command/button/Trigger.java | 15 +++-- .../wpilibj2/command/button/TriggerTest.java | 64 ++++++++++--------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index cc2d38d2aaa..aba7e39cf6c 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -25,7 +25,8 @@ */ public class Trigger implements BooleanSupplier { /** - * Enum specifying the initial state to use for a binding. This impacts whether or not the binding will be triggered immediately. + * Enum specifying the initial state to use for a binding. This impacts whether or not the binding + * will be triggered immediately. */ public enum InitialState { /** Indicates the binding should assume the condition starts as false. */ @@ -116,7 +117,8 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `false` to `true`. The command is never started immediately. + * Starts the given command whenever the condition changes from `false` to `true`. The command is + * never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained @@ -153,7 +155,8 @@ public void run() { } /** - * Starts the given command whenever the condition changes from `true` to `false`. The command is never started immediately. + * Starts the given command whenever the condition changes from `true` to `false`. The command is + * never started immediately. * * @param command the command to start * @return this trigger, so calls can be chained @@ -284,7 +287,8 @@ public void run() { } /** - * Toggles a command when the condition changes from `false` to `true`. The command is never toggled immediately. + * Toggles a command when the condition changes from `false` to `true`. The command is never + * toggled immediately. * * @param command the command to toggle * @return this trigger, so calls can be chained @@ -325,7 +329,8 @@ public void run() { } /** - * Toggles a command when the condition changes from `true` to `false`. The command is never toggled immediately. + * Toggles a command when the condition changes from `true` to `false`. The command is never + * toggled immediately. * * @param command the command to toggle * @return this trigger, so calls can be chained diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java index b2722cabf54..331f7f36095 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java @@ -13,9 +13,9 @@ import edu.wpi.first.wpilibj.simulation.SimHooks; import edu.wpi.first.wpilibj2.command.Command; -import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.CommandScheduler; import edu.wpi.first.wpilibj2.command.CommandTestBase; +import edu.wpi.first.wpilibj2.command.Commands; import edu.wpi.first.wpilibj2.command.FunctionalCommand; import edu.wpi.first.wpilibj2.command.RunCommand; import edu.wpi.first.wpilibj2.command.StartEndCommand; @@ -28,8 +28,8 @@ import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class TriggerTest extends CommandTestBase { @Test @@ -53,11 +53,12 @@ void onTrueTest() { @ParameterizedTest @MethodSource("initialStates") - void onTrueInitialStateTest(InitialState initialState, boolean pressed, Map results) { - CommandScheduler scheduler = CommandScheduler.getInstance(); - Command command1 = Commands.idle(); - InternalButton button = new InternalButton(); - boolean shouldBeScheduled = results.get("onTrue"); + void onTrueInitialStateTest( + InitialState initialState, boolean pressed, Map results) { + final CommandScheduler scheduler = CommandScheduler.getInstance(); + final Command command1 = Commands.idle(); + final InternalButton button = new InternalButton(); + final boolean shouldBeScheduled = results.get("onTrue"); button.setPressed(pressed); button.onTrue(command1, initialState); @@ -89,11 +90,12 @@ void onFalseTest() { @ParameterizedTest @MethodSource("initialStates") - void onFalseInitialStateTest(InitialState initialState, boolean pressed, Map results) { - CommandScheduler scheduler = CommandScheduler.getInstance(); - Command command1 = Commands.idle(); - InternalButton button = new InternalButton(); - boolean shouldBeScheduled = results.get("onFalse"); + void onFalseInitialStateTest( + InitialState initialState, boolean pressed, Map results) { + final CommandScheduler scheduler = CommandScheduler.getInstance(); + final Command command1 = Commands.idle(); + final InternalButton button = new InternalButton(); + final boolean shouldBeScheduled = results.get("onFalse"); button.setPressed(pressed); button.onFalse(command1, initialState); @@ -132,11 +134,12 @@ void onChangeTest() { @ParameterizedTest @MethodSource("initialStates") - void onChangeInitialStateTest(InitialState initialState, boolean pressed, Map results) { - CommandScheduler scheduler = CommandScheduler.getInstance(); - Command command1 = Commands.idle(); - InternalButton button = new InternalButton(); - boolean shouldBeScheduled = results.get("onTrue") || results.get("onFalse"); + void onChangeInitialStateTest( + InitialState initialState, boolean pressed, Map results) { + final CommandScheduler scheduler = CommandScheduler.getInstance(); + final Command command1 = Commands.idle(); + final InternalButton button = new InternalButton(); + final boolean shouldBeScheduled = results.get("onTrue") || results.get("onFalse"); button.setPressed(pressed); button.onChange(command1, initialState); @@ -256,11 +259,12 @@ void toggleOnTrueTest() { @ParameterizedTest @MethodSource("initialStates") - void toggleOnTrueInitialStateTest(InitialState initialState, boolean pressed, Map results) { - CommandScheduler scheduler = CommandScheduler.getInstance(); - Command command1 = Commands.idle(); - InternalButton button = new InternalButton(); - boolean shouldBeScheduled = results.get("onTrue"); + void toggleOnTrueInitialStateTest( + InitialState initialState, boolean pressed, Map results) { + final CommandScheduler scheduler = CommandScheduler.getInstance(); + final Command command1 = Commands.idle(); + final InternalButton button = new InternalButton(); + final boolean shouldBeScheduled = results.get("onTrue"); button.setPressed(pressed); button.onTrue(command1, initialState); @@ -354,13 +358,13 @@ void booleanSupplierTest() { static Stream initialStates() { return Stream.of( - arguments(InitialState.FALSE, true, Map.of("onTrue", true, "onFalse", false)), - arguments(InitialState.FALSE, false, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.TRUE, true, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.TRUE, false, Map.of("onTrue", false, "onFalse", true)), - arguments(InitialState.CONDITION, true, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.CONDITION, false, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.NEG_CONDITION, true, Map.of("onTrue", true, "onFalse", false)), - arguments(InitialState.NEG_CONDITION, false, Map.of("onTrue", false, "onFalse", true))); + arguments(InitialState.FALSE, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.FALSE, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.TRUE, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.TRUE, false, Map.of("onTrue", false, "onFalse", true)), + arguments(InitialState.CONDITION, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.CONDITION, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.NEG_CONDITION, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.NEG_CONDITION, false, Map.of("onTrue", false, "onFalse", true))); } } From 78b6a83b33709a07399c85825c9b95db1f53680b Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Fri, 22 Nov 2024 19:34:55 -0800 Subject: [PATCH 5/7] Improve InitialState constant docs --- .../wpilibj2/command/button/Trigger.java | 21 +++++++++++++++---- .../include/frc2/command/button/Trigger.h | 21 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index aba7e39cf6c..c8d16e627ca 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -29,13 +29,26 @@ public class Trigger implements BooleanSupplier { * will be triggered immediately. */ public enum InitialState { - /** Indicates the binding should assume the condition starts as false. */ + /** + * Indicates the binding should use false as the initial value. This causes a rising edge at the + * start if and only if the condition starts true. + */ FALSE, - /** Indicates the binding should assume the condition starts as true. */ + /** + * Indicates the binding should use true as the initial value. This causes a falling edge at the + * start if and only if the condition starts false. + */ TRUE, - /** Indicates the binding should use the trigger's condition. */ + /** + * Indicates the binding should use the trigger's condition as the initial value. This never + * causes an edge at the start. + */ CONDITION, - /** Indicates the binding should use the negated trigger's condition. */ + /** + * Indicates the binding should use the negated trigger's condition as the initial value. This + * always causes an edge at the start. Rising or falling depends on if the condition starts true + * or false, respectively. + */ NEG_CONDITION; } diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index ddc7b67932c..067317b4d7c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -35,13 +35,26 @@ class Trigger { * whether or not the binding will be triggered immediately. */ enum struct InitialState { - /** Indicates the binding should assume the condition starts as false. */ + /** + * Indicates the binding should use false as the initial value. This causes + * a rising edge at the start if and only if the condition starts true. + */ FALSE, - /** Indicates the binding should assume the condition starts as true. */ + /** + * Indicates the binding should use true as the initial value. This causes a + * falling edge at the start if and only if the condition starts false. + */ TRUE, - /** Indicates the binding should use the trigger's condition. */ + /** + * Indicates the binding should use the trigger's condition as the initial + * value. This never causes an edge at the start. + */ CONDITION, - /** Indicates the binding should use the negated trigger's condition. */ + /** + * Indicates the binding should use the negated trigger's condition as the + * initial value. This always causes an edge at the start. Rising or falling + * depends on if the condition starts true or false, respectively. + */ NEG_CONDITION }; From 861fc89e572fc4e8edcee71174b667651e6d0f68 Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Sat, 23 Nov 2024 09:20:28 -0800 Subject: [PATCH 6/7] Use kConstant instead of CONSTANT --- .../wpilibj2/command/button/Trigger.java | 30 ++++++------- .../include/frc2/command/button/Trigger.h | 44 +++++++++---------- .../wpilibj2/command/button/TriggerTest.java | 16 +++---- .../cpp/frc2/command/button/TriggerTest.cpp | 16 +++---- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index c8d16e627ca..8e7c68b5bbd 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -33,23 +33,23 @@ public enum InitialState { * Indicates the binding should use false as the initial value. This causes a rising edge at the * start if and only if the condition starts true. */ - FALSE, + kFalse, /** * Indicates the binding should use true as the initial value. This causes a falling edge at the * start if and only if the condition starts false. */ - TRUE, + kTrue, /** * Indicates the binding should use the trigger's condition as the initial value. This never * causes an edge at the start. */ - CONDITION, + kCondition, /** * Indicates the binding should use the negated trigger's condition as the initial value. This * always causes an edge at the start. Rising or falling depends on if the condition starts true * or false, respectively. */ - NEG_CONDITION; + kNegCondition; } private final BooleanSupplier m_condition; @@ -85,10 +85,10 @@ public Trigger(BooleanSupplier condition) { */ private boolean getInitialState(InitialState initialState) { return switch (initialState) { - case FALSE -> false; - case TRUE -> true; - case CONDITION -> m_condition.getAsBoolean(); - case NEG_CONDITION -> !m_condition.getAsBoolean(); + case kFalse -> false; + case kTrue -> true; + case kCondition -> m_condition.getAsBoolean(); + case kNegCondition -> !m_condition.getAsBoolean(); }; } @@ -99,7 +99,7 @@ private boolean getInitialState(InitialState initialState) { * @return this trigger, so calls can be chained */ public Trigger onChange(Command command) { - return onChange(command, InitialState.CONDITION); + return onChange(command, InitialState.kCondition); } /** @@ -137,7 +137,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger onTrue(Command command) { - return onTrue(command, InitialState.CONDITION); + return onTrue(command, InitialState.kCondition); } /** @@ -175,7 +175,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger onFalse(Command command) { - return onFalse(command, InitialState.CONDITION); + return onFalse(command, InitialState.kCondition); } /** @@ -216,7 +216,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileTrue(Command command) { - return whileTrue(command, InitialState.CONDITION); + return whileTrue(command, InitialState.kCondition); } /** @@ -263,7 +263,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger whileFalse(Command command) { - return whileFalse(command, InitialState.CONDITION); + return whileFalse(command, InitialState.kCondition); } /** @@ -307,7 +307,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger toggleOnTrue(Command command) { - return toggleOnTrue(command, InitialState.CONDITION); + return toggleOnTrue(command, InitialState.kCondition); } /** @@ -349,7 +349,7 @@ public void run() { * @return this trigger, so calls can be chained */ public Trigger toggleOnFalse(Command command) { - return toggleOnFalse(command, InitialState.CONDITION); + return toggleOnFalse(command, InitialState.kCondition); } /** diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index 067317b4d7c..452d048bdae 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -39,23 +39,23 @@ class Trigger { * Indicates the binding should use false as the initial value. This causes * a rising edge at the start if and only if the condition starts true. */ - FALSE, + kFalse, /** * Indicates the binding should use true as the initial value. This causes a * falling edge at the start if and only if the condition starts false. */ - TRUE, + kTrue, /** * Indicates the binding should use the trigger's condition as the initial * value. This never causes an edge at the start. */ - CONDITION, + kCondition, /** * Indicates the binding should use the negated trigger's condition as the * initial value. This always causes an edge at the start. Rising or falling * depends on if the condition starts true or false, respectively. */ - NEG_CONDITION + kNegCondition }; /** @@ -93,7 +93,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger OnChange(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the command when the condition changes. Moves command ownership to @@ -104,7 +104,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger OnChange(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command whenever the condition changes from `false` to @@ -118,7 +118,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger OnTrue(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command whenever the condition changes from `false` to @@ -129,7 +129,7 @@ class Trigger { * @return The trigger, for chained calls. */ Trigger OnTrue(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command whenever the condition changes from `true` to @@ -143,7 +143,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger OnFalse(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command whenever the condition changes from `true` to @@ -154,7 +154,7 @@ class Trigger { * @return The trigger, for chained calls. */ Trigger OnFalse(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command when the condition changes to `true` and cancels @@ -171,7 +171,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger WhileTrue(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command when the condition changes to `true` and cancels @@ -186,7 +186,7 @@ class Trigger { * @return The trigger, for chained calls. */ Trigger WhileTrue(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command when the condition changes to `false` and cancels @@ -203,7 +203,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger WhileFalse(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Starts the given command when the condition changes to `false` and cancels @@ -218,7 +218,7 @@ class Trigger { * @return The trigger, for chained calls. */ Trigger WhileFalse(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Toggles a command when the condition changes from `false` to `true`. @@ -231,7 +231,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Toggles a command when the condition changes from `false` to `true`. @@ -244,7 +244,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Toggles a command when the condition changes from `true` to the low @@ -258,7 +258,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(Command* command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Toggles a command when the condition changes from `true` to `false`. @@ -271,7 +271,7 @@ class Trigger { * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(CommandPtr&& command, - InitialState initialState = InitialState::CONDITION); + InitialState initialState = InitialState::kCondition); /** * Composes two triggers with logical AND. @@ -349,13 +349,13 @@ class Trigger { private: bool GetInitialState(InitialState initialState) const { switch (initialState) { - case InitialState::FALSE: + case InitialState::kFalse: return false; - case InitialState::TRUE: + case InitialState::kTrue: return true; - case InitialState::CONDITION: + case InitialState::kCondition: return m_condition(); - case InitialState::NEG_CONDITION: + case InitialState::kNegCondition: return !m_condition(); } return false; diff --git a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java index 331f7f36095..a5e5eacf06c 100644 --- a/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java +++ b/wpilibNewCommands/src/test/java/edu/wpi/first/wpilibj2/command/button/TriggerTest.java @@ -358,13 +358,13 @@ void booleanSupplierTest() { static Stream initialStates() { return Stream.of( - arguments(InitialState.FALSE, true, Map.of("onTrue", true, "onFalse", false)), - arguments(InitialState.FALSE, false, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.TRUE, true, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.TRUE, false, Map.of("onTrue", false, "onFalse", true)), - arguments(InitialState.CONDITION, true, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.CONDITION, false, Map.of("onTrue", false, "onFalse", false)), - arguments(InitialState.NEG_CONDITION, true, Map.of("onTrue", true, "onFalse", false)), - arguments(InitialState.NEG_CONDITION, false, Map.of("onTrue", false, "onFalse", true))); + arguments(InitialState.kFalse, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.kFalse, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.kTrue, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.kTrue, false, Map.of("onTrue", false, "onFalse", true)), + arguments(InitialState.kCondition, true, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.kCondition, false, Map.of("onTrue", false, "onFalse", false)), + arguments(InitialState.kNegCondition, true, Map.of("onTrue", true, "onFalse", false)), + arguments(InitialState.kNegCondition, false, Map.of("onTrue", false, "onFalse", true))); } } diff --git a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp index c4008fc7630..0a3c4ff2092 100644 --- a/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp +++ b/wpilibNewCommands/src/test/native/cpp/frc2/command/button/TriggerTest.cpp @@ -325,11 +325,11 @@ INSTANTIATE_TEST_SUITE_P( InitialStateTests, TriggerInitialStateTest, testing::Values( // Initial state, pressed, rising, falling - std::tuple{Trigger::InitialState::FALSE, false, false, false}, - std::tuple{Trigger::InitialState::FALSE, true, true, false}, - std::tuple{Trigger::InitialState::TRUE, false, false, true}, - std::tuple{Trigger::InitialState::TRUE, true, false, false}, - std::tuple{Trigger::InitialState::CONDITION, false, false, false}, - std::tuple{Trigger::InitialState::CONDITION, true, false, false}, - std::tuple{Trigger::InitialState::NEG_CONDITION, false, false, true}, - std::tuple{Trigger::InitialState::NEG_CONDITION, true, true, false})); + std::tuple{Trigger::InitialState::kFalse, false, false, false}, + std::tuple{Trigger::InitialState::kFalse, true, true, false}, + std::tuple{Trigger::InitialState::kTrue, false, false, true}, + std::tuple{Trigger::InitialState::kTrue, true, false, false}, + std::tuple{Trigger::InitialState::kCondition, false, false, false}, + std::tuple{Trigger::InitialState::kCondition, true, false, false}, + std::tuple{Trigger::InitialState::kNegCondition, false, false, true}, + std::tuple{Trigger::InitialState::kNegCondition, true, true, false})); From d33b081af1e44e701c2b256a750252791861b9f1 Mon Sep 17 00:00:00 2001 From: Joseph Eng Date: Sat, 23 Nov 2024 09:30:50 -0800 Subject: [PATCH 7/7] Document default InitialState --- .../wpilibj2/command/button/Trigger.java | 16 +++---- .../include/frc2/command/button/Trigger.h | 45 ++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java index 8e7c68b5bbd..4689d023252 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Trigger.java @@ -41,7 +41,7 @@ public enum InitialState { kTrue, /** * Indicates the binding should use the trigger's condition as the initial value. This never - * causes an edge at the start. + * causes an edge at the start. This is the default behavior. */ kCondition, /** @@ -106,7 +106,7 @@ public Trigger onChange(Command command) { * Starts the command when the condition changes. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger onChange(Command command, InitialState initialState) { @@ -144,7 +144,7 @@ public Trigger onTrue(Command command) { * Starts the given command whenever the condition changes from `false` to `true`. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger onTrue(Command command, InitialState initialState) { @@ -182,7 +182,7 @@ public Trigger onFalse(Command command) { * Starts the given command whenever the condition changes from `true` to `false`. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger onFalse(Command command, InitialState initialState) { @@ -227,7 +227,7 @@ public Trigger whileTrue(Command command) { * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger whileTrue(Command command, InitialState initialState) { @@ -274,7 +274,7 @@ public Trigger whileFalse(Command command) { * should restart, see {@link edu.wpi.first.wpilibj2.command.RepeatCommand}. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger whileFalse(Command command, InitialState initialState) { @@ -314,7 +314,7 @@ public Trigger toggleOnTrue(Command command) { * Toggles a command when the condition changes from `false` to `true`. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger toggleOnTrue(Command command, InitialState initialState) { @@ -356,7 +356,7 @@ public Trigger toggleOnFalse(Command command) { * Toggles a command when the condition changes from `true` to `false`. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) by default * @return this trigger, so calls can be chained */ public Trigger toggleOnFalse(Command command, InitialState initialState) { diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h index 452d048bdae..0349d9a2a25 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -47,7 +47,8 @@ class Trigger { kTrue, /** * Indicates the binding should use the trigger's condition as the initial - * value. This never causes an edge at the start. + * value. This never causes an edge at the start. This is the default + * behavior. */ kCondition, /** @@ -89,7 +90,8 @@ class Trigger { * Starts the command when the condition changes. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger OnChange(Command* command, @@ -100,7 +102,8 @@ class Trigger { * the button scheduler. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger OnChange(CommandPtr&& command, @@ -114,7 +117,8 @@ class Trigger { * lifespan of the command. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger OnTrue(Command* command, @@ -125,7 +129,8 @@ class Trigger { * `true`. Moves command ownership to the button scheduler. * * @param command The command to bind. - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return The trigger, for chained calls. */ Trigger OnTrue(CommandPtr&& command, @@ -139,7 +144,8 @@ class Trigger { * lifespan of the command. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger OnFalse(Command* command, @@ -150,7 +156,8 @@ class Trigger { * `false`. * * @param command The command to bind. - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return The trigger, for chained calls. */ Trigger OnFalse(CommandPtr&& command, @@ -167,7 +174,8 @@ class Trigger { * lifespan of the command. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger WhileTrue(Command* command, @@ -182,7 +190,8 @@ class Trigger { * `true`. If the command should restart, see RepeatCommand. * * @param command The command to bind. - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return The trigger, for chained calls. */ Trigger WhileTrue(CommandPtr&& command, @@ -199,7 +208,8 @@ class Trigger { * lifespan of the command. * * @param command the command to start - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger WhileFalse(Command* command, @@ -214,7 +224,8 @@ class Trigger { * `false`. If the command should restart, see RepeatCommand. * * @param command The command to bind. - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return The trigger, for chained calls. */ Trigger WhileFalse(CommandPtr&& command, @@ -227,7 +238,8 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(Command* command, @@ -240,7 +252,8 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(CommandPtr&& command, @@ -254,7 +267,8 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(Command* command, @@ -267,7 +281,8 @@ class Trigger { * lifespan of the command. * * @param command the command to toggle - * @param initialState the initial state to use + * @param initialState the initial state to use, kCondition (no initial edge) + * by default * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(CommandPtr&& command,