diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java index e286a74229..84850a03b4 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/CommandScheduler.java @@ -158,7 +158,7 @@ public final class CommandScheduler implements NTSendable, AutoCloseable { */ @Deprecated(since = "2023") public void addButton(Runnable button) { - m_activeButtonLoop.bind(() -> true, requireNonNullParam(button, "button", "addButton")); + m_activeButtonLoop.bind(requireNonNullParam(button, "button", "addButton")); } /** diff --git a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java index 60917d049f..4e748ced32 100644 --- a/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java +++ b/wpilibNewCommands/src/main/java/edu/wpi/first/wpilibj2/command/button/Button.java @@ -159,8 +159,7 @@ public class Button extends Trigger { * * @param command the command to start * @return this button, so calls can be chained - * @deprecated Instead, pass {@link #rising()} as an end condition to {@link - * Command#until(BooleanSupplier)}. + * @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}. */ @Deprecated public Button cancelWhenPressed(final Command command) { 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 3513ab6c1a..e3e0e224a2 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 @@ -13,7 +13,6 @@ import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.CommandScheduler; import edu.wpi.first.wpilibj2.command.InstantCommand; import edu.wpi.first.wpilibj2.command.Subsystem; -import java.util.function.BiFunction; import java.util.function.BooleanSupplier; /** @@ -28,7 +27,8 @@ import java.util.function.BooleanSupplier; *

This class is provided by the NewCommands VendorDep */ public class Trigger implements BooleanSupplier { - private final BooleanEvent m_event; + private final BooleanSupplier m_condition; + private final EventLoop m_loop; /** * Creates a new trigger based on the given condition. @@ -37,18 +37,8 @@ public class Trigger implements BooleanSupplier { * @param condition the condition represented by this trigger */ public Trigger(EventLoop loop, BooleanSupplier condition) { - m_event = new BooleanEvent(loop, condition); - } - - /** - * Copies the BooleanEvent into a Trigger object. - * - * @param toCast the BooleanEvent - * @return a Trigger wrapping the given BooleanEvent - * @see BooleanEvent#castTo(BiFunction) - */ - public static Trigger cast(BooleanEvent toCast) { - return toCast.castTo(Trigger::new); + m_loop = requireNonNullParam(loop, "loop", "Trigger"); + m_condition = requireNonNullParam(condition, "condition", "Trigger"); } /** @@ -73,11 +63,24 @@ public class Trigger implements BooleanSupplier { * * @param command the command to start * @return this trigger, so calls can be chained - * @see #rising() */ public Trigger onTrue(Command command) { requireNonNullParam(command, "command", "onRising"); - m_event.rising().ifHigh(command::schedule); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + command.schedule(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -86,11 +89,24 @@ public class Trigger implements BooleanSupplier { * * @param command the command to start * @return this trigger, so calls can be chained - * @see #falling() */ public Trigger onFalse(Command command) { requireNonNullParam(command, "command", "onFalling"); - m_event.falling().ifHigh(command::schedule); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (m_pressedLast && !pressed) { + command.schedule(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -106,8 +122,23 @@ public class Trigger implements BooleanSupplier { */ public Trigger whileTrue(Command command) { requireNonNullParam(command, "command", "whileHigh"); - m_event.rising().ifHigh(command::schedule); - m_event.falling().ifHigh(command::cancel); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + command.schedule(); + } else if (m_pressedLast && !pressed) { + command.cancel(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -123,8 +154,23 @@ public class Trigger implements BooleanSupplier { */ public Trigger whileFalse(Command command) { requireNonNullParam(command, "command", "whileLow"); - m_event.falling().ifHigh(command::schedule); - m_event.rising().ifHigh(command::cancel); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (m_pressedLast && !pressed) { + command.schedule(); + } else if (!m_pressedLast && pressed) { + command.cancel(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -136,16 +182,25 @@ public class Trigger implements BooleanSupplier { */ public Trigger toggleOnTrue(Command command) { requireNonNullParam(command, "command", "toggleOnRising"); - m_event - .rising() - .ifHigh( - () -> { - if (!command.isScheduled()) { - command.schedule(); - } else { + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + if (command.isScheduled()) { command.cancel(); + } else { + command.schedule(); } - }); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -157,16 +212,25 @@ public class Trigger implements BooleanSupplier { */ public Trigger toggleOnFalse(Command command) { requireNonNullParam(command, "command", "toggleOnFalling"); - m_event - .falling() - .ifHigh( - () -> { - if (!command.isScheduled()) { - command.schedule(); - } else { + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (m_pressedLast && !pressed) { + if (command.isScheduled()) { command.cancel(); + } else { + command.schedule(); } - }); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -181,7 +245,21 @@ public class Trigger implements BooleanSupplier { public Trigger whenActive(final Command command) { requireNonNullParam(command, "command", "whenActive"); - m_event.rising().ifHigh(command::schedule); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + command.schedule(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -215,8 +293,23 @@ public class Trigger implements BooleanSupplier { public Trigger whileActiveContinuous(final Command command) { requireNonNullParam(command, "command", "whileActiveContinuous"); - m_event.ifHigh(command::schedule); - m_event.falling().ifHigh(command::cancel); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (pressed) { + command.schedule(); + } else if (m_pressedLast) { + command.cancel(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -246,9 +339,23 @@ public class Trigger implements BooleanSupplier { public Trigger whileActiveOnce(final Command command) { requireNonNullParam(command, "command", "whileActiveOnce"); - m_event.rising().ifHigh(command::schedule); - m_event.falling().ifHigh(command::cancel); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + command.schedule(); + } else if (m_pressedLast && !pressed) { + command.cancel(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -263,7 +370,21 @@ public class Trigger implements BooleanSupplier { public Trigger whenInactive(final Command command) { requireNonNullParam(command, "command", "whenInactive"); - m_event.falling().ifHigh(command::schedule); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (m_pressedLast && !pressed) { + command.schedule(); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -292,16 +413,25 @@ public class Trigger implements BooleanSupplier { public Trigger toggleWhenActive(final Command command) { requireNonNullParam(command, "command", "toggleWhenActive"); - m_event - .rising() - .ifHigh( - () -> { + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { if (command.isScheduled()) { command.cancel(); } else { command.schedule(); } - }); + } + + m_pressedLast = pressed; + } + }); return this; } @@ -311,57 +441,94 @@ public class Trigger implements BooleanSupplier { * * @param command the command to cancel * @return this trigger, so calls can be chained - * @deprecated Instead, pass {@link #rising()} as an end condition to {@link - * Command#until(BooleanSupplier)}. + * @deprecated Instead, pass this as an end condition to {@link Command#until(BooleanSupplier)}. */ @Deprecated public Trigger cancelWhenActive(final Command command) { requireNonNullParam(command, "command", "cancelWhenActive"); - m_event.rising().ifHigh(command::cancel); + m_loop.bind( + new Runnable() { + private boolean m_pressedLast = m_condition.getAsBoolean(); + + @Override + public void run() { + boolean pressed = m_condition.getAsBoolean(); + + if (!m_pressedLast && pressed) { + command.cancel(); + } + + m_pressedLast = pressed; + } + }); return this; } - /** - * Get the wrapped BooleanEvent. - * - * @return the wrapped BooleanEvent instance. - */ - public BooleanEvent getEvent() { - return m_event; - } - @Override public boolean getAsBoolean() { - return m_event.getAsBoolean(); + return m_condition.getAsBoolean(); } + /** + * Composes two triggers with logical OR. + * + * @param trigger the condition to compose with + * @return A trigger which is active when either component trigger is active. + */ public Trigger and(BooleanSupplier trigger) { - return cast(m_event.and(trigger)); + return new Trigger(() -> m_condition.getAsBoolean() && trigger.getAsBoolean()); } + /** + * Composes two triggers with logical OR. + * + * @param trigger the condition to compose with + * @return A trigger which is active when either component trigger is active. + */ public Trigger or(BooleanSupplier trigger) { - return cast(m_event.or(trigger)); + return new Trigger(() -> m_condition.getAsBoolean() || trigger.getAsBoolean()); } + /** + * Creates a new trigger that is active when this trigger is inactive, i.e. that acts as the + * negation of this trigger. + * + * @return the negated trigger + */ public Trigger negate() { - return cast(m_event.negate()); + return new Trigger(() -> !m_condition.getAsBoolean()); } + /** + * Creates a new debounced trigger from this trigger - it will become active when this trigger has + * been active for longer than the specified period. + * + * @param seconds The debounce period. + * @return The debounced trigger (rising edges debounced only) + */ public Trigger debounce(double seconds) { return debounce(seconds, Debouncer.DebounceType.kRising); } + /** + * Creates a new debounced trigger from this trigger - it will become active when this trigger has + * been active for longer than the specified period. + * + * @param seconds The debounce period. + * @param type The debounce type. + * @return The debounced trigger. + */ public Trigger debounce(double seconds, Debouncer.DebounceType type) { - return cast(m_event.debounce(seconds, type)); - } + return new Trigger( + new BooleanSupplier() { + final Debouncer m_debouncer = new Debouncer(seconds, type); - public Trigger rising() { - return cast(m_event.rising()); - } - - public Trigger falling() { - return cast(m_event.falling()); + @Override + public boolean getAsBoolean() { + return m_debouncer.calculate(m_condition.getAsBoolean()); + } + }); } } 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 dc225aca33..3908daf13b 100644 --- a/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp +++ b/wpilibNewCommands/src/main/native/cpp/frc2/command/button/Trigger.cpp @@ -14,101 +14,200 @@ using namespace frc2; Trigger::Trigger(const Trigger& other) = default; Trigger Trigger::OnTrue(Command* command) { - m_event.Rising().IfHigh([command] { command->Schedule(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (!previous && current) { + command->Schedule(); + } + + previous = current; + }); return *this; } Trigger Trigger::OnTrue(CommandPtr&& command) { - m_event.Rising().IfHigh( - [command = std::move(command)] { command.Schedule(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (!previous && current) { + command.Schedule(); + } + + previous = current; + }); return *this; } Trigger Trigger::OnFalse(Command* command) { - m_event.Falling().IfHigh([command] { command->Schedule(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (previous && !current) { + command->Schedule(); + } + + previous = current; + }); return *this; } Trigger Trigger::OnFalse(CommandPtr&& command) { - m_event.Falling().IfHigh( - [command = std::move(command)] { command.Schedule(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (previous && !current) { + command.Schedule(); + } + + previous = current; + }); return *this; } Trigger Trigger::WhileTrue(Command* command) { - m_event.Rising().IfHigh([command] { command->Schedule(); }); - m_event.Falling().IfHigh([command] { command->Cancel(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (!previous && current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } + + previous = current; + }); return *this; } Trigger Trigger::WhileTrue(CommandPtr&& command) { - auto ptr = std::make_shared(std::move(command)); - m_event.Rising().IfHigh([ptr] { ptr->Schedule(); }); - m_event.Falling().IfHigh([ptr] { ptr->Cancel(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (!previous && current) { + command.Schedule(); + } else if (previous && !current) { + command.Cancel(); + } + + previous = current; + }); return *this; } Trigger Trigger::WhileFalse(Command* command) { - m_event.Falling().IfHigh([command] { command->Schedule(); }); - m_event.Rising().IfHigh([command] { command->Cancel(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (previous && !current) { + command->Schedule(); + } else if (!previous && current) { + command->Cancel(); + } + + previous = current; + }); return *this; } Trigger Trigger::WhileFalse(CommandPtr&& command) { - auto ptr = std::make_shared(std::move(command)); - m_event.Falling().IfHigh([ptr] { ptr->Schedule(); }); - m_event.Rising().IfHigh([ptr] { ptr->Cancel(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (!previous && current) { + command.Schedule(); + } else if (previous && !current) { + command.Cancel(); + } + + previous = current; + }); return *this; } Trigger Trigger::ToggleOnTrue(Command* command) { - m_event.Rising().IfHigh([command] { - if (command->IsScheduled()) { - command->Cancel(); - } else { - command->Schedule(); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = command]() mutable { + bool current = condition(); + + if (!previous && current) { + if (command->IsScheduled()) { + command->Cancel(); + } else { + command->Schedule(); + } } + + previous = current; }); return *this; } Trigger Trigger::ToggleOnTrue(CommandPtr&& command) { - m_event.Rising().IfHigh([command = std::move(command)] { - if (command.IsScheduled()) { - command.Cancel(); - } else { - command.Schedule(); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (!previous && current) { + if (command.IsScheduled()) { + command.Cancel(); + } else { + command.Schedule(); + } } + + previous = current; }); return *this; } Trigger Trigger::ToggleOnFalse(Command* command) { - m_event.Falling().IfHigh([command] { - if (command->IsScheduled()) { - command->Cancel(); - } else { - command->Schedule(); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = command]() mutable { + bool current = condition(); + + if (previous && !current) { + if (command->IsScheduled()) { + command->Cancel(); + } else { + command->Schedule(); + } } + + previous = current; }); return *this; } Trigger Trigger::ToggleOnFalse(CommandPtr&& command) { - m_event.Falling().IfHigh([command = std::move(command)] { - if (command.IsScheduled()) { - command.Cancel(); - } else { - command.Schedule(); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (previous && !current) { + if (command.IsScheduled()) { + command.Cancel(); + } else { + command.Schedule(); + } } + + previous = current; }); return *this; } WPI_IGNORE_DEPRECATED Trigger Trigger::WhenActive(Command* command) { - m_event.Rising().IfHigh([command] { command->Schedule(); }); - return *this; + return OnTrue(command); } Trigger Trigger::WhenActive(std::function toRun, @@ -123,8 +222,18 @@ Trigger Trigger::WhenActive(std::function toRun, } Trigger Trigger::WhileActiveContinous(Command* command) { - m_event.IfHigh([command] { command->Schedule(); }); - m_event.Falling().IfHigh([command] { command->Cancel(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } + + previous = current; + }); return *this; } @@ -141,13 +250,32 @@ Trigger Trigger::WhileActiveContinous( } Trigger Trigger::WhileActiveOnce(Command* command) { - m_event.Rising().IfHigh([command] { command->Schedule(); }); - m_event.Falling().IfHigh([command] { command->Cancel(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (!previous && current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } + + previous = current; + }); return *this; } Trigger Trigger::WhenInactive(Command* command) { - m_event.Falling().IfHigh([command] { command->Schedule(); }); + m_loop->Bind( + [condition = m_condition, previous = m_condition(), command]() mutable { + bool current = condition(); + + if (previous && !current) { + command->Schedule(); + } + + previous = current; + }); return *this; } @@ -163,22 +291,42 @@ Trigger Trigger::WhenInactive(std::function toRun, } Trigger Trigger::ToggleWhenActive(Command* command) { - m_event.Rising().IfHigh([command] { - if (command->IsScheduled()) { - command->Cancel(); - } else { - command->Schedule(); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = command]() mutable { + bool current = condition(); + + if (!previous && current) { + if (command->IsScheduled()) { + command->Cancel(); + } else { + command->Schedule(); + } } + + previous = current; }); return *this; } Trigger Trigger::CancelWhenActive(Command* command) { - m_event.Rising().IfHigh([command] { command->Cancel(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::move(command)]() mutable { + bool current = condition(); + + if (!previous && current) { + command->Cancel(); + } + + previous = current; + }); return *this; } WPI_UNIGNORE_DEPRECATED -BooleanEvent Trigger::GetEvent() const { - return m_event; +Trigger Trigger::Debounce(units::second_t debounceTime, + frc::Debouncer::DebounceType type) { + return Trigger(m_loop, [debouncer = frc::Debouncer(debounceTime, type), + condition = m_condition]() mutable { + return debouncer.Calculate(condition()); + }); } diff --git a/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h b/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h index bb19ee9d16..8a9794409c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Button.h @@ -266,10 +266,9 @@ class Button : public Trigger { * * @param command The command to bind. * @return The button, for chained calls. - * @deprecated Use Rising() as a command end condition with Until() instead. + * @deprecated Pass this as a command end condition with Until() instead. */ - WPI_DEPRECATED( - "Use Rising() as a command end condition with Until() instead.") + WPI_DEPRECATED("Pass this as a command end condition with Until() instead.") Button CancelWhenPressed(Command* command); }; } // namespace frc2 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 4a7cecc042..c3a5e86b5c 100644 --- a/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h +++ b/wpilibNewCommands/src/main/native/include/frc2/command/button/Trigger.h @@ -42,7 +42,7 @@ class Trigger { * @param condition the condition represented by this trigger */ explicit Trigger(std::function condition) - : m_event{CommandScheduler::GetInstance().GetDefaultButtonLoop(), + : Trigger{CommandScheduler::GetInstance().GetDefaultButtonLoop(), std::move(condition)} {} /** @@ -52,7 +52,7 @@ class Trigger { * @param condition the condition represented by this trigger */ Trigger(frc::EventLoop* loop, std::function condition) - : m_event{loop, std::move(condition)} {} + : m_loop{loop}, m_condition{std::move(condition)} {} /** * Create a new trigger that is always `false`. @@ -70,7 +70,6 @@ class Trigger { * * @param command the command to start * @return this trigger, so calls can be chained - * @see #Rising() */ Trigger OnTrue(Command* command); @@ -92,7 +91,6 @@ class Trigger { * * @param command the command to start * @return this trigger, so calls can be chained - * @see #Falling() */ Trigger OnFalse(Command* command); @@ -233,10 +231,17 @@ class Trigger { Command, std::remove_reference_t>>> WPI_DEPRECATED("Use OnTrue(Command) instead") Trigger WhenActive(T&& command) { - m_event.Rising().IfHigh( - [command = std::make_unique>( - std::forward(command))] { command->Schedule(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::make_unique>( + std::forward(command))]() mutable { + bool current = condition(); + if (!previous && current) { + command->Schedule(); + } + + previous = current; + }); return *this; } @@ -298,10 +303,19 @@ class Trigger { "Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule " "with IfHigh(std::function).") Trigger WhileActiveContinous(T&& command) { - std::shared_ptr ptr = - std::make_shared>(std::forward(command)); - m_event.IfHigh([ptr] { ptr->Schedule(); }); - m_event.Falling().IfHigh([ptr] { ptr->Cancel(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::make_unique>( + std::forward(command))]() mutable { + bool current = condition(); + + if (current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } + + previous = current; + }); return *this; } @@ -354,12 +368,19 @@ class Trigger { Command, std::remove_reference_t>>> WPI_DEPRECATED("Use WhileTrue(Command) instead.") Trigger WhileActiveOnce(T&& command) { - std::shared_ptr ptr = - std::make_shared>(std::forward(command)); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::make_unique>( + std::forward(command))]() mutable { + bool current = condition(); - m_event.Rising().IfHigh([ptr] { ptr->Schedule(); }); - m_event.Falling().IfHigh([ptr] { ptr->Cancel(); }); + if (!previous && current) { + command->Schedule(); + } else if (previous && !current) { + command->Cancel(); + } + previous = current; + }); return *this; } @@ -389,10 +410,17 @@ class Trigger { Command, std::remove_reference_t>>> WPI_DEPRECATED("Use OnFalse(Command) instead.") Trigger WhenInactive(T&& command) { - m_event.Falling().IfHigh( - [command = std::make_unique>( - std::forward(command))] { command->Schedule(); }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::make_unique>( + std::forward(command))]() mutable { + bool current = condition(); + if (previous && !current) { + command->Schedule(); + } + + previous = current; + }); return *this; } @@ -448,15 +476,21 @@ class Trigger { Command, std::remove_reference_t>>> WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.") Trigger ToggleWhenActive(T&& command) { - m_event.Rising().IfHigh( - [command = std::make_unique>( - std::forward(command))] { - if (!command->IsScheduled()) { - command->Schedule(); - } else { - command->Cancel(); - } - }); + m_loop->Bind([condition = m_condition, previous = m_condition(), + command = std::make_unique>( + std::forward(command))]() mutable { + bool current = condition(); + + if (!previous && current) { + if (command->IsScheduled()) { + command->Cancel(); + } else { + command->Schedule(); + } + } + + previous = current; + }); return *this; } @@ -468,33 +502,20 @@ class Trigger { * * @param command The command to bind. * @return The trigger, for chained calls. - * @deprecated Use Rising() as a command end condition with Until() instead. + * @deprecated Pass this as a command end condition with Until() instead. */ - WPI_DEPRECATED( - "Use Rising() as a command end condition with Until() instead.") + WPI_DEPRECATED("Pass this as a command end condition with Until() instead.") Trigger CancelWhenActive(Command* command); - /** - * Get a new event that events only when this one newly changes to true. - * - * @return a new event representing when this one newly changes to true. - */ - Trigger Rising() { return m_event.Rising().CastTo(); } - - /** - * Get a new event that triggers only when this one newly changes to false. - * - * @return a new event representing when this one newly changes to false. - */ - Trigger Falling() { return m_event.Falling().CastTo(); } - /** * Composes two triggers with logical AND. * * @return A trigger which is active when both component triggers are active. */ Trigger operator&&(std::function rhs) { - return m_event.operator&&(rhs).CastTo(); + return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] { + return condition() && rhs(); + }); } /** @@ -502,8 +523,10 @@ class Trigger { * * @return A trigger which is active when both component triggers are active. */ - Trigger operator&&(Trigger& rhs) { - return (m_event && rhs.m_event).CastTo(); + Trigger operator&&(Trigger rhs) { + return Trigger(m_loop, [condition = m_condition, rhs] { + return condition() && rhs.m_condition(); + }); } /** @@ -512,7 +535,9 @@ class Trigger { * @return A trigger which is active when either component trigger is active. */ Trigger operator||(std::function rhs) { - return m_event.operator||(rhs).CastTo(); + return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] { + return condition() || rhs(); + }); } /** @@ -520,8 +545,10 @@ class Trigger { * * @return A trigger which is active when either component trigger is active. */ - Trigger operator||(Trigger& rhs) { - return (m_event || rhs.m_event).CastTo(); + Trigger operator||(Trigger rhs) { + return Trigger(m_loop, [condition = m_condition, rhs] { + return condition() || rhs.m_condition(); + }); } /** @@ -530,7 +557,9 @@ class Trigger { * @return A trigger which is active when the component trigger is inactive, * and vice-versa. */ - Trigger operator!() { return m_event.operator!().CastTo(); } + Trigger operator!() { + return Trigger(m_loop, [condition = m_condition] { return !condition(); }); + } /** * Creates a new debounced trigger from this trigger - it will become active @@ -542,16 +571,10 @@ class Trigger { */ Trigger Debounce(units::second_t debounceTime, frc::Debouncer::DebounceType type = - frc::Debouncer::DebounceType::kRising) { - return m_event.Debounce(debounceTime, type).CastTo(); - } - - /** - * Get the wrapped BooleanEvent instance. - */ - frc::BooleanEvent GetEvent() const; + frc::Debouncer::DebounceType::kRising); private: - frc::BooleanEvent m_event; + frc::EventLoop* m_loop; + std::function m_condition; }; } // namespace frc2 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 106e066135..82d359c3aa 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 @@ -178,7 +178,7 @@ class TriggerTest extends CommandTestBase { InternalButton button = new InternalButton(); Command command1 = new StartEndCommand(startCounter::incrementAndGet, endCounter::incrementAndGet) - .until(button.rising()); + .until(button); button.setPressed(false); command1.schedule(); diff --git a/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp b/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp index 28ab0fca14..5b8ce63d68 100644 --- a/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp +++ b/wpilibc/src/main/native/cpp/event/BooleanEvent.cpp @@ -17,8 +17,12 @@ bool BooleanEvent::GetAsBoolean() const { return m_condition(); } -void BooleanEvent::IfHigh(wpi::unique_function action) { - m_loop->Bind(m_condition, std::move(action)); +void BooleanEvent::IfHigh(std::function action) { + m_loop->Bind([condition = m_condition, action = std::move(action)] { + if (condition()) { + action(); + } + }); } BooleanEvent BooleanEvent::operator!() { diff --git a/wpilibc/src/main/native/cpp/event/EventLoop.cpp b/wpilibc/src/main/native/cpp/event/EventLoop.cpp index 71cff7066c..5af79c96d3 100644 --- a/wpilibc/src/main/native/cpp/event/EventLoop.cpp +++ b/wpilibc/src/main/native/cpp/event/EventLoop.cpp @@ -8,20 +8,13 @@ using namespace frc; EventLoop::EventLoop() {} -void EventLoop::Binding::Poll() { - if (condition()) { - action(); - } -} - -void EventLoop::Bind(std::function condition, - wpi::unique_function action) { - m_bindings.emplace_back(Binding{condition, std::move(action)}); +void EventLoop::Bind(wpi::unique_function action) { + m_bindings.emplace_back(std::move(action)); } void EventLoop::Poll() { - for (Binding& binding : m_bindings) { - binding.Poll(); + for (wpi::unique_function& action : m_bindings) { + action(); } } diff --git a/wpilibc/src/main/native/include/frc/event/BooleanEvent.h b/wpilibc/src/main/native/include/frc/event/BooleanEvent.h index d550543e47..745a53c095 100644 --- a/wpilibc/src/main/native/include/frc/event/BooleanEvent.h +++ b/wpilibc/src/main/native/include/frc/event/BooleanEvent.h @@ -19,7 +19,7 @@ namespace frc { /** * This class provides an easy way to link actions to inputs. Each object * represents a boolean condition to which callback actions can be bound using - * {@link #IfHigh(wpi::unique_function)}. + * {@link #IfHigh(std::function)}. * *

These events can easily be composed using factories such as {@link * #operator!}, @@ -51,7 +51,7 @@ class BooleanEvent { * * @param action the action to run if this event is active. */ - void IfHigh(wpi::unique_function action); + void IfHigh(std::function action); operator std::function(); // NOLINT diff --git a/wpilibc/src/main/native/include/frc/event/EventLoop.h b/wpilibc/src/main/native/include/frc/event/EventLoop.h index 11ead4fadc..d18fac3fe3 100644 --- a/wpilibc/src/main/native/include/frc/event/EventLoop.h +++ b/wpilibc/src/main/native/include/frc/event/EventLoop.h @@ -20,13 +20,11 @@ class EventLoop { EventLoop& operator=(const EventLoop&) = delete; /** - * Bind a new action to run whenever the condition is true. + * Bind a new action to run. * - * @param condition the condition to listen to. * @param action the action to run. */ - void Bind(std::function condition, - wpi::unique_function action); + void Bind(wpi::unique_function action); /** * Poll all bindings. @@ -39,12 +37,6 @@ class EventLoop { void Clear(); private: - struct Binding { - std::function condition; - wpi::unique_function action; - - void Poll(); - }; - std::vector m_bindings; + std::vector> m_bindings; }; } // namespace frc diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java index 742a7a8b30..a810006563 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/BooleanEvent.java @@ -54,7 +54,12 @@ public class BooleanEvent implements BooleanSupplier { * @param action the action to run if this event is active. */ public final void ifHigh(Runnable action) { - m_loop.bind(m_signal, action); + m_loop.bind( + () -> { + if (m_signal.getAsBoolean()) { + action.run(); + } + }); } /** diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java index 88eed86044..3e92c019ab 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/event/EventLoop.java @@ -6,45 +6,27 @@ package edu.wpi.first.wpilibj.event; import java.util.Collection; import java.util.LinkedHashSet; -import java.util.function.BooleanSupplier; /** The loop polling {@link BooleanEvent} objects and executing the actions bound to them. */ public final class EventLoop { - private final Collection m_bindings = new LinkedHashSet<>(); + private final Collection m_bindings = new LinkedHashSet<>(); /** * Bind a new action to run whenever the condition is true. * - * @param condition the condition to listen to. * @param action the action to run. */ - public void bind(BooleanSupplier condition, Runnable action) { - m_bindings.add(new Binding(condition, action)); + public void bind(Runnable action) { + m_bindings.add(action); } /** Poll all bindings. */ public void poll() { - m_bindings.forEach(Binding::poll); + m_bindings.forEach(Runnable::run); } /** Clear all bindings. */ public void clear() { m_bindings.clear(); } - - private static class Binding { - private final BooleanSupplier m_condition; - private final Runnable m_action; - - private Binding(BooleanSupplier condition, Runnable action) { - this.m_condition = condition; - this.m_action = action; - } - - void poll() { - if (m_condition.getAsBoolean()) { - m_action.run(); - } - } - } } diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java index 7f0bb1e95e..ea3c569e65 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/event/EventLoopTest.java @@ -16,8 +16,8 @@ class EventLoopTest { var counterTrue = new AtomicInteger(0); var counterFalse = new AtomicInteger(0); var loop = new EventLoop(); - loop.bind(() -> true, counterTrue::incrementAndGet); - loop.bind(() -> false, counterFalse::incrementAndGet); + new BooleanEvent(loop, () -> true).ifHigh(counterTrue::incrementAndGet); + new BooleanEvent(loop, () -> false).ifHigh(counterFalse::incrementAndGet); assertEquals(0, counterTrue.get()); assertEquals(0, counterFalse.get()); @@ -40,7 +40,7 @@ class EventLoopTest { var loop = new EventLoop(); // first ensure binding works - loop.bind(condition::get, counter::incrementAndGet); + new BooleanEvent(loop, condition::get).ifHigh(counter::incrementAndGet); condition.set(false); loop.poll();