// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #pragma once #include #include #include #include #include #include #include #include #include #include #include "frc2/command/Command.h" #include "frc2/command/CommandScheduler.h" namespace frc2 { class Command; /** * This class provides an easy way to link commands to conditions. * *

It is very easy to link a button to a command. For instance, you could * link the trigger button of a joystick to a "score" command. * *

Triggers can easily be composed for advanced functionality using the * {@link #operator!}, {@link #operator||}, {@link #operator&&} operators. * *

This class is provided by the NewCommands VendorDep */ class Trigger { public: /** * Creates a new trigger based on the given condition. * *

Polled by the default scheduler button loop. * * @param condition the condition represented by this trigger */ explicit Trigger(std::function condition) : Trigger{CommandScheduler::GetInstance().GetDefaultButtonLoop(), std::move(condition)} {} /** * Creates a new trigger based on the given condition. * * @param loop The loop instance that polls this trigger. * @param condition the condition represented by this trigger */ Trigger(frc::EventLoop* loop, std::function condition) : m_loop{loop}, m_condition{std::move(condition)} {} /** * Create a new trigger that is always `false`. */ Trigger() : Trigger([] { return false; }) {} Trigger(const Trigger& other); /** * Starts the given command whenever the condition changes from `false` to * `true`. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to start * @return this trigger, so calls can be chained */ Trigger OnTrue(Command* command); /** * 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. * @return The trigger, for chained calls. */ Trigger OnTrue(CommandPtr&& command); /** * Starts the given command whenever the condition changes from `true` to * `false`. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to start * @return this trigger, so calls can be chained */ Trigger OnFalse(Command* command); /** * Starts the given command whenever the condition changes from `true` to * `false`. * * @param command The command to bind. * @return The trigger, for chained calls. */ Trigger OnFalse(CommandPtr&& command); /** * 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 RepeatCommand. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to start * @return this trigger, so calls can be chained */ Trigger WhileTrue(Command* command); /** * Starts the given command when the condition changes to `true` and cancels * it when the condition changes to `false`. Moves command ownership to the * button scheduler. * *

Doesn't re-start the command if it ends while the condition is still * `true`. If the command should restart, see RepeatCommand. * * @param command The command to bind. * @return The trigger, for chained calls. */ Trigger WhileTrue(CommandPtr&& command); /** * 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 * `true`. If the command should restart, see RepeatCommand. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to start * @return this trigger, so calls can be chained */ Trigger WhileFalse(Command* command); /** * Starts the given command when the condition changes to `false` and cancels * it when the condition changes to `true`. Moves command ownership to the * button scheduler. * *

Doesn't re-start the command if it ends while the condition is still * `false`. If the command should restart, see RepeatCommand. * * @param command The command to bind. * @return The trigger, for chained calls. */ Trigger WhileFalse(CommandPtr&& command); /** * Toggles a command when the condition changes from `false` to `true`. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to toggle * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(Command* command); /** * Toggles a command when the condition changes from `false` to `true`. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to toggle * @return this trigger, so calls can be chained */ Trigger ToggleOnTrue(CommandPtr&& command); /** * Toggles a command when the condition changes from `true` to the low * state. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to toggle * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(Command* command); /** * Toggles a command when the condition changes from `true` to `false`. * *

Takes a raw pointer, and so is non-owning; users are responsible for the * lifespan of the command. * * @param command the command to toggle * @return this trigger, so calls can be chained */ Trigger ToggleOnFalse(CommandPtr&& command); /** * Binds a command to start when the trigger becomes active. Takes a * raw pointer, and so is non-owning; users are responsible for the lifespan * of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use OnTrue(Command) instead */ WPI_DEPRECATED("Use OnTrue(Command) instead") Trigger WhenActive(Command* command); /** * Binds a command to start when the trigger becomes active. Transfers * command ownership to the button scheduler, so the user does not have to * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be * *copied.* * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use OnTrue(Command) instead */ template >>> WPI_DEPRECATED("Use OnTrue(Command) instead") Trigger WhenActive(T&& command) { 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; } /** * Binds a runnable to execute when the trigger becomes active. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use OnTrue(Command) instead and construct the InstantCommand * manually */ WPI_DEPRECATED( "Use OnTrue(Command) instead and construct the InstantCommand manually") Trigger WhenActive(std::function toRun, std::initializer_list requirements); /** * Binds a runnable to execute when the trigger becomes active. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use OnTrue(Command) instead and construct the InstantCommand * manually */ WPI_DEPRECATED( "Use OnTrue(Command) instead and construct the InstantCommand manually") Trigger WhenActive(std::function toRun, std::span requirements = {}); /** * Binds a command to be started repeatedly while the trigger is active, and * canceled when it becomes inactive. Takes a raw pointer, and so is * non-owning; users are responsible for the lifespan of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule with IfHigh(std::function). */ WPI_DEPRECATED( "Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule " "with IfHigh(std::function).") Trigger WhileActiveContinous(Command* command); /** * Binds a command to be started repeatedly while the trigger is active, and * canceled when it becomes inactive. Transfers command ownership to the * button scheduler, so the user does not have to worry about lifespan - * rvalue refs will be *moved*, lvalue refs will be *copied.* * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule with IfHigh(std::function). */ template >>> WPI_DEPRECATED( "Use WhileTrue(Command) with RepeatCommand, or bind command::Schedule " "with IfHigh(std::function).") Trigger WhileActiveContinous(T&& command) { 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; } /** * Binds a runnable to execute repeatedly while the trigger is active. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use WhileTrue(Command) and construct a RunCommand manually */ WPI_DEPRECATED("Use WhileTrue(Command) and construct a RunCommand manually") Trigger WhileActiveContinous(std::function toRun, std::initializer_list requirements); /** * Binds a runnable to execute repeatedly while the trigger is active. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use WhileTrue(Command) and construct a RunCommand manually */ WPI_DEPRECATED("Use WhileTrue(Command) and construct a RunCommand manually") Trigger WhileActiveContinous(std::function toRun, std::span requirements = {}); /** * Binds a command to be started when the trigger becomes active, and * canceled when it becomes inactive. Takes a raw pointer, and so is * non-owning; users are responsible for the lifespan of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use WhileTrue(Command) instead. */ WPI_DEPRECATED("Use WhileTrue(Command) instead.") Trigger WhileActiveOnce(Command* command); /** * Binds a command to be started when the trigger becomes active, and * canceled when it becomes inactive. Transfers command ownership to the * button scheduler, so the user does not have to worry about lifespan - * rvalue refs will be *moved*, lvalue refs will be *copied.* * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use WhileTrue(Command) instead. */ template >>> WPI_DEPRECATED("Use WhileTrue(Command) instead.") Trigger WhileActiveOnce(T&& command) { 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(); } else if (previous && !current) { command->Cancel(); } previous = current; }); return *this; } /** * Binds a command to start when the trigger becomes inactive. Takes a * raw pointer, and so is non-owning; users are responsible for the lifespan * of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use OnFalse(Command) instead. */ WPI_DEPRECATED("Use OnFalse(Command) instead.") Trigger WhenInactive(Command* command); /** * Binds a command to start when the trigger becomes inactive. Transfers * command ownership to the button scheduler, so the user does not have to * worry about lifespan - rvalue refs will be *moved*, lvalue refs will be * *copied.* * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use OnFalse(Command) instead. */ template >>> WPI_DEPRECATED("Use OnFalse(Command) instead.") Trigger WhenInactive(T&& command) { 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; } /** * Binds a runnable to execute when the trigger becomes inactive. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use OnFalse(Command) instead and construct the InstantCommand * manually */ WPI_DEPRECATED( "Use OnFalse(Command) instead and construct the InstantCommand manually") Trigger WhenInactive(std::function toRun, std::initializer_list requirements); /** * Binds a runnable to execute when the trigger becomes inactive. * * @param toRun the runnable to execute. * @param requirements the required subsystems. * @deprecated Use OnFalse(Command) instead and construct the InstantCommand * manually */ WPI_DEPRECATED( "Use OnFalse(Command) instead and construct the InstantCommand manually") Trigger WhenInactive(std::function toRun, std::span requirements = {}); /** * Binds a command to start when the trigger becomes active, and be canceled * when it again becomes active. Takes a raw pointer, and so is non-owning; * users are responsible for the lifespan of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use ToggleOnTrue(Command) instead. */ WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.") Trigger ToggleWhenActive(Command* command); /** * Binds a command to start when the trigger becomes active, and be canceled * when it again becomes active. Transfers command ownership to the button * scheduler, so the user does not have to worry about lifespan - rvalue refs * will be *moved*, lvalue refs will be *copied.* * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Use ToggleOnTrue(Command) instead. */ template >>> WPI_DEPRECATED("Use ToggleOnTrue(Command) instead.") Trigger ToggleWhenActive(T&& command) { 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; } /** * Binds a command to be canceled when the trigger becomes active. Takes a * raw pointer, and so is non-owning; users are responsible for the lifespan * and scheduling of the command. * * @param command The command to bind. * @return The trigger, for chained calls. * @deprecated Pass this as a command end condition with Until() instead. */ WPI_DEPRECATED("Pass this as a command end condition with Until() instead.") Trigger CancelWhenActive(Command* command); /** * Composes two triggers with logical AND. * * @return A trigger which is active when both component triggers are active. */ Trigger operator&&(std::function rhs) { return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] { return condition() && rhs(); }); } /** * Composes two triggers with logical AND. * * @return A trigger which is active when both component triggers are active. */ Trigger operator&&(Trigger rhs) { return Trigger(m_loop, [condition = m_condition, rhs] { return condition() && rhs.m_condition(); }); } /** * Composes two triggers with logical OR. * * @return A trigger which is active when either component trigger is active. */ Trigger operator||(std::function rhs) { return Trigger(m_loop, [condition = m_condition, rhs = std::move(rhs)] { return condition() || rhs(); }); } /** * Composes two triggers with logical OR. * * @return A trigger which is active when either component trigger is active. */ Trigger operator||(Trigger rhs) { return Trigger(m_loop, [condition = m_condition, rhs] { return condition() || rhs.m_condition(); }); } /** * Composes a trigger with logical NOT. * * @return A trigger which is active when the component trigger is inactive, * and vice-versa. */ Trigger operator!() { return Trigger(m_loop, [condition = m_condition] { return !condition(); }); } /** * 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 debounceTime The debounce period. * @param type The debounce type. * @return The debounced trigger. */ Trigger Debounce(units::second_t debounceTime, frc::Debouncer::DebounceType type = frc::Debouncer::DebounceType::kRising); private: frc::EventLoop* m_loop; std::function m_condition; }; } // namespace frc2