mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-20 00:51:42 +00:00
[commands] Revamp Interruptible (#4192)
This commit is contained in:
@@ -91,20 +91,36 @@ class Command {
|
||||
virtual bool IsFinished() { return false; }
|
||||
|
||||
/**
|
||||
* Specifies the set of subsystems used by this command. Two commands cannot
|
||||
* use the same subsystem at the same time. If the command is scheduled as
|
||||
* interruptible and another command is scheduled that shares a requirement,
|
||||
* the command will be interrupted. Else, the command will not be scheduled.
|
||||
* If no subsystems are required, return an empty set.
|
||||
* Specifies the set of subsystems used by this command. Two commands cannot
|
||||
* use the same subsystem at the same time. If another command is scheduled
|
||||
* that shares a requirement, GetInterruptionBehavior() will be checked and
|
||||
* followed. If no subsystems are required, return an empty set.
|
||||
*
|
||||
* <p>Note: it is recommended that user implementations contain the
|
||||
* requirements as a field, and return that field here, rather than allocating
|
||||
* a new set every time this is called.
|
||||
*
|
||||
* @return the set of subsystems that are required
|
||||
* @see InterruptionBehavior
|
||||
*/
|
||||
virtual wpi::SmallSet<Subsystem*, 4> GetRequirements() const = 0;
|
||||
|
||||
/**
|
||||
* An enum describing the command's behavior when another command with a
|
||||
* shared requirement is scheduled.
|
||||
*/
|
||||
enum class InterruptionBehavior {
|
||||
/**
|
||||
* This command ends, End(true) is called, and the incoming command is
|
||||
* scheduled normally.
|
||||
*
|
||||
* <p>This is the default behavior.
|
||||
*/
|
||||
kCancelSelf,
|
||||
/** This command continues, and the incoming command is not scheduled. */
|
||||
kCancelIncoming
|
||||
};
|
||||
|
||||
/**
|
||||
* Decorates this command with a timeout. If the specified timeout is
|
||||
* exceeded before the command finishes normally, the command will be
|
||||
@@ -238,21 +254,22 @@ class Command {
|
||||
virtual std::unique_ptr<Command> IgnoringDisable(bool doesRunWhenDisabled) &&;
|
||||
|
||||
/**
|
||||
* Schedules this command.
|
||||
* Decorates this command to run or stop when disabled.
|
||||
*
|
||||
* @param interruptible whether this command can be interrupted by another
|
||||
* command that shares one of its requirements
|
||||
* @param interruptBehavior true to run when disabled.
|
||||
* @return the decorated command
|
||||
*/
|
||||
void Schedule(bool interruptible);
|
||||
virtual std::unique_ptr<Command> WithInterruptBehavior(
|
||||
InterruptionBehavior interruptBehavior) &&;
|
||||
|
||||
/**
|
||||
* Schedules this command, defaulting to interruptible.
|
||||
* Schedules this command.
|
||||
*/
|
||||
void Schedule() { Schedule(true); }
|
||||
void Schedule();
|
||||
|
||||
/**
|
||||
* Cancels this command. Will call the command's interrupted() method.
|
||||
* Commands will be canceled even if they are not marked as interruptible.
|
||||
* Cancels this command. Will call End(true). Commands will be canceled
|
||||
* regardless of interruption behavior.
|
||||
*/
|
||||
void Cancel();
|
||||
|
||||
@@ -296,6 +313,16 @@ class Command {
|
||||
*/
|
||||
virtual bool RunsWhenDisabled() const { return false; }
|
||||
|
||||
/**
|
||||
* How the command behaves when another command with a shared requirement is
|
||||
* scheduled.
|
||||
*
|
||||
* @return a variant of InterruptionBehavior, defaulting to kCancelSelf.
|
||||
*/
|
||||
virtual InterruptionBehavior GetInterruptionBehavior() const {
|
||||
return InterruptionBehavior::kCancelSelf;
|
||||
}
|
||||
|
||||
virtual std::string GetName() const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -83,60 +83,27 @@ class CommandScheduler final : public nt::NTSendable,
|
||||
void ClearButtons();
|
||||
|
||||
/**
|
||||
* Schedules a command for execution. Does nothing if the command is already
|
||||
* Schedules a command for execution. Does nothing if the command is already
|
||||
* scheduled. If a command's requirements are not available, it will only be
|
||||
* started if all the commands currently using those requirements have been
|
||||
* scheduled as interruptible. If this is the case, they will be interrupted
|
||||
* scheduled as interruptible. If this is the case, they will be interrupted
|
||||
* and the command will be scheduled.
|
||||
*
|
||||
* @param interruptible whether this command can be interrupted
|
||||
* @param command the command to schedule
|
||||
*/
|
||||
void Schedule(bool interruptible, Command* command);
|
||||
|
||||
/**
|
||||
* Schedules a command for execution, with interruptible defaulted to true.
|
||||
* Does nothing if the command is already scheduled.
|
||||
*
|
||||
* @param command the command to schedule
|
||||
*/
|
||||
void Schedule(Command* command);
|
||||
|
||||
/**
|
||||
* Schedules multiple commands for execution. Does nothing if the command is
|
||||
* already scheduled. If a command's requirements are not available, it will
|
||||
* only be started if all the commands currently using those requirements have
|
||||
* been scheduled as interruptible. If this is the case, they will be
|
||||
* interrupted and the command will be scheduled.
|
||||
*
|
||||
* @param interruptible whether the commands should be interruptible
|
||||
* @param commands the commands to schedule
|
||||
*/
|
||||
void Schedule(bool interruptible, wpi::span<Command* const> commands);
|
||||
|
||||
/**
|
||||
* Schedules multiple commands for execution. Does nothing if the command is
|
||||
* already scheduled. If a command's requirements are not available, it will
|
||||
* only be started if all the commands currently using those requirements have
|
||||
* been scheduled as interruptible. If this is the case, they will be
|
||||
* interrupted and the command will be scheduled.
|
||||
*
|
||||
* @param interruptible whether the commands should be interruptible
|
||||
* @param commands the commands to schedule
|
||||
*/
|
||||
void Schedule(bool interruptible, std::initializer_list<Command*> commands);
|
||||
|
||||
/**
|
||||
* Schedules multiple commands for execution, with interruptible defaulted to
|
||||
* true. Does nothing if the command is already scheduled.
|
||||
* Schedules multiple commands for execution. Does nothing for commands
|
||||
* already scheduled.
|
||||
*
|
||||
* @param commands the commands to schedule
|
||||
*/
|
||||
void Schedule(wpi::span<Command* const> commands);
|
||||
|
||||
/**
|
||||
* Schedules multiple commands for execution, with interruptible defaulted to
|
||||
* true. Does nothing if the command is already scheduled.
|
||||
* Schedules multiple commands for execution. Does nothing for commands
|
||||
* already scheduled.
|
||||
*
|
||||
* @param commands the commands to schedule
|
||||
*/
|
||||
@@ -262,17 +229,6 @@ class CommandScheduler final : public nt::NTSendable,
|
||||
*/
|
||||
void CancelAll();
|
||||
|
||||
/**
|
||||
* Returns the time since a given command was scheduled. Note that this only
|
||||
* works on commands that are directly scheduled by the scheduler; it will not
|
||||
* work on commands inside of commandgroups, as the scheduler does not see
|
||||
* them.
|
||||
*
|
||||
* @param command the command to query
|
||||
* @return the time since the command was scheduled
|
||||
*/
|
||||
units::second_t TimeSinceScheduled(const Command* command) const;
|
||||
|
||||
/**
|
||||
* Whether the given commands are running. Note that this only works on
|
||||
* commands that are directly scheduled by the scheduler; it will not work on
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// 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 <units/time.h>
|
||||
|
||||
namespace frc2 {
|
||||
/**
|
||||
* Class that holds scheduling state for a command. Used internally by the
|
||||
* CommandScheduler
|
||||
*
|
||||
* This class is provided by the NewCommands VendorDep
|
||||
*/
|
||||
class CommandState final {
|
||||
public:
|
||||
CommandState() = default;
|
||||
|
||||
explicit CommandState(bool interruptible);
|
||||
|
||||
bool IsInterruptible() const { return m_interruptible; }
|
||||
|
||||
// The time since this command was initialized.
|
||||
units::second_t TimeSinceInitialized() const;
|
||||
|
||||
private:
|
||||
units::second_t m_startTime = -1_s;
|
||||
bool m_interruptible;
|
||||
|
||||
void StartTiming();
|
||||
void StartRunning();
|
||||
};
|
||||
} // namespace frc2
|
||||
@@ -64,6 +64,10 @@ class WrapperCommand : public CommandHelper<CommandBase, WrapperCommand> {
|
||||
|
||||
bool RunsWhenDisabled() const override;
|
||||
|
||||
InterruptionBehavior GetInterruptionBehavior() const override;
|
||||
|
||||
wpi::SmallSet<Subsystem*, 4> GetRequirements() const override;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Command> m_command;
|
||||
};
|
||||
|
||||
@@ -43,10 +43,9 @@ class Button : public Trigger {
|
||||
* of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Button WhenPressed(Command* command, bool interruptible = true);
|
||||
Button WhenPressed(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the button is pressed. Transfers
|
||||
@@ -55,13 +54,12 @@ class Button : public Trigger {
|
||||
* *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Button WhenPressed(T&& command, bool interruptible = true) {
|
||||
WhenActive(std::forward<T>(command), interruptible);
|
||||
Button WhenPressed(T&& command) {
|
||||
WhenActive(std::forward<T>(command));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -89,10 +87,9 @@ class Button : public Trigger {
|
||||
* users are responsible for the lifespan of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
Button WhileHeld(Command* command, bool interruptible = true);
|
||||
Button WhileHeld(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to be started repeatedly while the button is pressed, and
|
||||
@@ -101,13 +98,12 @@ class Button : public Trigger {
|
||||
* will be *moved*, lvalue refs will be *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Button WhileHeld(T&& command, bool interruptible = true) {
|
||||
WhileActiveContinous(std::forward<T>(command), interruptible);
|
||||
Button WhileHeld(T&& command) {
|
||||
WhileActiveContinous(std::forward<T>(command));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -135,10 +131,9 @@ class Button : public Trigger {
|
||||
* responsible for the lifespan of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
Button WhenHeld(Command* command, bool interruptible = true);
|
||||
Button WhenHeld(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to be started when the button is pressed, and canceled
|
||||
@@ -147,13 +142,12 @@ class Button : public Trigger {
|
||||
* *moved*, lvalue refs will be *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Button WhenHeld(T&& command, bool interruptible = true) {
|
||||
WhileActiveOnce(std::forward<T>(command), interruptible);
|
||||
Button WhenHeld(T&& command) {
|
||||
WhileActiveOnce(std::forward<T>(command));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -163,10 +157,9 @@ class Button : public Trigger {
|
||||
* of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
Button WhenReleased(Command* command, bool interruptible = true);
|
||||
Button WhenReleased(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the button is pressed. Transfers
|
||||
@@ -175,13 +168,12 @@ class Button : public Trigger {
|
||||
* *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Button WhenReleased(T&& command, bool interruptible = true) {
|
||||
WhenInactive(std::forward<T>(command), interruptible);
|
||||
Button WhenReleased(T&& command) {
|
||||
WhenInactive(std::forward<T>(command));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -209,10 +201,9 @@ class Button : public Trigger {
|
||||
* responsible for the lifespan of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
Button ToggleWhenPressed(Command* command, bool interruptible = true);
|
||||
Button ToggleWhenPressed(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the button is pressed, and be canceled when
|
||||
@@ -221,13 +212,12 @@ class Button : public Trigger {
|
||||
* *moved*, lvalue refs will be *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The button, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Button ToggleWhenPressed(T&& command, bool interruptible = true) {
|
||||
ToggleWhenActive(std::forward<T>(command), interruptible);
|
||||
Button ToggleWhenPressed(T&& command) {
|
||||
ToggleWhenActive(std::forward<T>(command));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,33 +60,30 @@ class Trigger : public frc::BooleanEvent {
|
||||
Trigger(const Trigger& other);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the trigger becomes active. Takes a
|
||||
* 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.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Trigger WhenActive(Command* command, bool interruptible = true);
|
||||
Trigger WhenActive(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the trigger becomes active. Transfers
|
||||
* 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.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Trigger WhenActive(T&& command, bool interruptible = true) {
|
||||
Trigger WhenActive(T&& command) {
|
||||
this->Rising().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command)),
|
||||
interruptible] { command->Schedule(interruptible); });
|
||||
std::forward<T>(command))] { command->Schedule(); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -115,10 +112,9 @@ class Trigger : public frc::BooleanEvent {
|
||||
* non-owning; users are responsible for the lifespan of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Trigger WhileActiveContinous(Command* command, bool interruptible = true);
|
||||
Trigger WhileActiveContinous(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to be started repeatedly while the trigger is active, and
|
||||
@@ -127,15 +123,14 @@ class Trigger : public frc::BooleanEvent {
|
||||
* rvalue refs will be *moved*, lvalue refs will be *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Trigger WhileActiveContinous(T&& command, bool interruptible = true) {
|
||||
Trigger WhileActiveContinous(T&& command) {
|
||||
std::shared_ptr<T> ptr =
|
||||
std::make_shared<std::remove_reference_t<T>>(std::forward<T>(command));
|
||||
this->IfHigh([ptr, interruptible] { ptr->Schedule(interruptible); });
|
||||
this->IfHigh([ptr] { ptr->Schedule(); });
|
||||
this->Falling().IfHigh([ptr] { ptr->Cancel(); });
|
||||
|
||||
return *this;
|
||||
@@ -165,29 +160,26 @@ class Trigger : public frc::BooleanEvent {
|
||||
* non-owning; users are responsible for the lifespan of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Trigger WhileActiveOnce(Command* command, bool interruptible = true);
|
||||
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
|
||||
* 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.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Trigger WhileActiveOnce(T&& command, bool interruptible = true) {
|
||||
Trigger WhileActiveOnce(T&& command) {
|
||||
std::shared_ptr<T> ptr =
|
||||
std::make_shared<std::remove_reference_t<T>>(std::forward<T>(command));
|
||||
|
||||
this->Rising().IfHigh(
|
||||
[ptr, interruptible] { ptr->Schedule(interruptible); });
|
||||
this->Rising().IfHigh([ptr] { ptr->Schedule(); });
|
||||
this->Falling().IfHigh([ptr] { ptr->Cancel(); });
|
||||
|
||||
return *this;
|
||||
@@ -199,10 +191,9 @@ class Trigger : public frc::BooleanEvent {
|
||||
* of the command.
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Trigger WhenInactive(Command* command, bool interruptible = true);
|
||||
Trigger WhenInactive(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the trigger becomes inactive. Transfers
|
||||
@@ -211,16 +202,14 @@ class Trigger : public frc::BooleanEvent {
|
||||
* *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Trigger WhenInactive(T&& command, bool interruptible = true) {
|
||||
Trigger WhenInactive(T&& command) {
|
||||
this->Falling().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command)),
|
||||
interruptible] { command->Schedule(interruptible); });
|
||||
std::forward<T>(command))] { command->Schedule(); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -245,14 +234,13 @@ class Trigger : public frc::BooleanEvent {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
* 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.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
Trigger ToggleWhenActive(Command* command, bool interruptible = true);
|
||||
Trigger ToggleWhenActive(Command* command);
|
||||
|
||||
/**
|
||||
* Binds a command to start when the trigger becomes active, and be canceled
|
||||
@@ -261,18 +249,16 @@ class Trigger : public frc::BooleanEvent {
|
||||
* will be *moved*, lvalue refs will be *copied.*
|
||||
*
|
||||
* @param command The command to bind.
|
||||
* @param interruptible Whether the command should be interruptible.
|
||||
* @return The trigger, for chained calls.
|
||||
*/
|
||||
template <class T, typename = std::enable_if_t<std::is_base_of_v<
|
||||
Command, std::remove_reference_t<T>>>>
|
||||
Trigger ToggleWhenActive(T&& command, bool interruptible = true) {
|
||||
Trigger ToggleWhenActive(T&& command) {
|
||||
this->Rising().IfHigh(
|
||||
[command = std::make_unique<std::remove_reference_t<T>>(
|
||||
std::forward<T>(command)),
|
||||
interruptible] {
|
||||
std::forward<T>(command))] {
|
||||
if (!command->IsScheduled()) {
|
||||
command->Schedule(interruptible);
|
||||
command->Schedule();
|
||||
} else {
|
||||
command->Cancel();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user