[commands] Revamp Interruptible (#4192)

This commit is contained in:
Starlight220
2022-08-30 07:53:47 +03:00
committed by GitHub
parent f2a8d38d2a
commit c3a93fb995
26 changed files with 369 additions and 592 deletions

View File

@@ -67,6 +67,27 @@ std::unique_ptr<Command> Command::IgnoringDisable(bool doesRunWhenDisabled) && {
std::move(*this).TransferOwnership(), doesRunWhenDisabled);
}
std::unique_ptr<Command> Command::WithInterruptBehavior(
InterruptionBehavior interruptBehavior) && {
class InterruptBehaviorCommand
: public CommandHelper<WrapperCommand, InterruptBehaviorCommand> {
public:
InterruptBehaviorCommand(std::unique_ptr<Command>&& command,
InterruptionBehavior interruptBehavior)
: CommandHelper(std::move(command)),
m_interruptBehavior(interruptBehavior) {}
InterruptionBehavior GetInterruptionBehavior() const override {
return m_interruptBehavior;
}
private:
InterruptionBehavior m_interruptBehavior;
};
return std::make_unique<InterruptBehaviorCommand>(
std::move(*this).TransferOwnership(), interruptBehavior);
}
ParallelRaceGroup Command::WithInterrupt(std::function<bool()> condition) && {
std::vector<std::unique_ptr<Command>> temp;
temp.emplace_back(std::make_unique<WaitUntilCommand>(std::move(condition)));
@@ -130,8 +151,8 @@ ConditionalCommand Command::Unless(std::function<bool()> condition) && {
std::move(condition));
}
void Command::Schedule(bool interruptible) {
CommandScheduler::GetInstance().Schedule(interruptible, this);
void Command::Schedule() {
CommandScheduler::GetInstance().Schedule(this);
}
void Command::Cancel() {

View File

@@ -19,16 +19,14 @@
#include <wpi/sendable/SendableRegistry.h>
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandState.h"
#include "frc2/command/Subsystem.h"
using namespace frc2;
class CommandScheduler::Impl {
public:
// A map from commands to their scheduling state. Also used as a set of the
// currently-running commands.
wpi::DenseMap<Command*, CommandState> scheduledCommands;
// A set of the currently-running commands.
wpi::SmallSet<Command*, 12> scheduledCommands;
// A map from required subsystems to their requiring commands. Also used as a
// set of the currently-required subsystems.
@@ -56,7 +54,7 @@ class CommandScheduler::Impl {
// scheduled/canceled during run
bool inRunLoop = false;
wpi::DenseMap<Command*, bool> toSchedule;
wpi::SmallVector<Command*, 4> toSchedule;
wpi::SmallVector<Command*, 4> toCancel;
};
@@ -112,9 +110,9 @@ void CommandScheduler::ClearButtons() {
m_impl->activeButtonLoop->Clear();
}
void CommandScheduler::Schedule(bool interruptible, Command* command) {
void CommandScheduler::Schedule(Command* command) {
if (m_impl->inRunLoop) {
m_impl->toSchedule.try_emplace(command, interruptible);
m_impl->toSchedule.emplace_back(command);
return;
}
@@ -126,7 +124,7 @@ void CommandScheduler::Schedule(bool interruptible, Command* command) {
}
if (m_impl->disabled ||
(frc::RobotState::IsDisabled() && !command->RunsWhenDisabled()) ||
ContainsKey(m_impl->scheduledCommands, command)) {
m_impl->scheduledCommands.contains(command)) {
return;
}
@@ -139,8 +137,8 @@ void CommandScheduler::Schedule(bool interruptible, Command* command) {
for (auto&& i1 : m_impl->requirements) {
if (requirements.find(i1.first) != requirements.end()) {
isDisjoint = false;
allInterruptible &=
m_impl->scheduledCommands[i1.second].IsInterruptible();
allInterruptible &= (i1.second->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf);
intersection.emplace_back(i1.second);
}
}
@@ -151,7 +149,7 @@ void CommandScheduler::Schedule(bool interruptible, Command* command) {
Cancel(cmdToCancel);
}
}
m_impl->scheduledCommands[command] = CommandState{interruptible};
m_impl->scheduledCommands.insert(command);
for (auto&& requirement : requirements) {
m_impl->requirements[requirement] = command;
}
@@ -163,33 +161,15 @@ void CommandScheduler::Schedule(bool interruptible, Command* command) {
}
}
void CommandScheduler::Schedule(Command* command) {
Schedule(true, command);
}
void CommandScheduler::Schedule(bool interruptible,
wpi::span<Command* const> commands) {
for (auto command : commands) {
Schedule(interruptible, command);
}
}
void CommandScheduler::Schedule(bool interruptible,
std::initializer_list<Command*> commands) {
for (auto command : commands) {
Schedule(interruptible, command);
}
}
void CommandScheduler::Schedule(wpi::span<Command* const> commands) {
for (auto command : commands) {
Schedule(true, command);
Schedule(command);
}
}
void CommandScheduler::Schedule(std::initializer_list<Command*> commands) {
for (auto command : commands) {
Schedule(true, command);
Schedule(command);
}
}
@@ -218,10 +198,7 @@ void CommandScheduler::Run() {
m_impl->inRunLoop = true;
// Run scheduled commands, remove finished commands.
for (auto iterator = m_impl->scheduledCommands.begin();
iterator != m_impl->scheduledCommands.end(); iterator++) {
Command* command = iterator->getFirst();
for (Command* command : m_impl->scheduledCommands) {
if (!command->RunsWhenDisabled() && frc::RobotState::IsDisabled()) {
Cancel(command);
continue;
@@ -243,14 +220,14 @@ void CommandScheduler::Run() {
m_impl->requirements.erase(requirement);
}
m_impl->scheduledCommands.erase(iterator);
m_impl->scheduledCommands.erase(command);
m_watchdog.AddEpoch(command->GetName() + ".End(false)");
}
}
m_impl->inRunLoop = false;
for (auto&& commandInterruptible : m_impl->toSchedule) {
Schedule(commandInterruptible.second, commandInterruptible.first);
for (Command* command : m_impl->toSchedule) {
Schedule(command);
}
for (auto&& command : m_impl->toCancel) {
@@ -336,7 +313,7 @@ void CommandScheduler::Cancel(Command* command) {
if (find == m_impl->scheduledCommands.end()) {
return;
}
m_impl->scheduledCommands.erase(find);
m_impl->scheduledCommands.erase(*find);
for (auto&& requirement : m_impl->requirements) {
if (requirement.second == command) {
m_impl->requirements.erase(requirement.first);
@@ -364,20 +341,11 @@ void CommandScheduler::Cancel(std::initializer_list<Command*> commands) {
void CommandScheduler::CancelAll() {
wpi::SmallVector<Command*, 16> commands;
for (auto&& command : m_impl->scheduledCommands) {
commands.emplace_back(command.first);
commands.emplace_back(command);
}
Cancel(commands);
}
units::second_t CommandScheduler::TimeSinceScheduled(
const Command* command) const {
auto find = m_impl->scheduledCommands.find(command);
if (find != m_impl->scheduledCommands.end()) {
return find->second.TimeSinceInitialized();
} else {
return -1_s;
}
}
bool CommandScheduler::IsScheduled(
wpi::span<const Command* const> commands) const {
for (auto command : commands) {
@@ -399,8 +367,7 @@ bool CommandScheduler::IsScheduled(
}
bool CommandScheduler::IsScheduled(const Command* command) const {
return m_impl->scheduledCommands.find(command) !=
m_impl->scheduledCommands.end();
return m_impl->scheduledCommands.contains(command);
}
Command* CommandScheduler::Requiring(const Subsystem* subsystem) const {
@@ -458,9 +425,9 @@ void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) {
wpi::SmallVector<std::string, 8> names;
wpi::SmallVector<double, 8> ids;
for (auto&& command : m_impl->scheduledCommands) {
names.emplace_back(command.first->GetName());
uintptr_t ptrTmp = reinterpret_cast<uintptr_t>(command.first);
for (Command* command : m_impl->scheduledCommands) {
names.emplace_back(command->GetName());
uintptr_t ptrTmp = reinterpret_cast<uintptr_t>(command);
ids.emplace_back(static_cast<double>(ptrTmp));
}
nt::NetworkTableEntry(namesEntry).SetStringArray(names);
@@ -470,5 +437,13 @@ void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) {
void CommandScheduler::SetDefaultCommandImpl(Subsystem* subsystem,
std::unique_ptr<Command> command) {
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelIncoming) {
std::puts(
"Registering a non-interruptible default command!\n"
"This will likely prevent any other commands from "
"requiring this subsystem.");
// Warn, but allow -- there might be a use case for this.
}
m_impl->subsystems[subsystem] = std::move(command);
}

View File

@@ -1,27 +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.
#include "frc2/command/CommandState.h"
#include <frc/Timer.h>
using namespace frc2;
CommandState::CommandState(bool interruptible)
: m_interruptible{interruptible} {
StartTiming();
StartRunning();
}
void CommandState::StartTiming() {
m_startTime = frc::Timer::GetFPGATimestamp();
}
void CommandState::StartRunning() {
m_startTime = -1_s;
}
units::second_t CommandState::TimeSinceInitialized() const {
return m_startTime != -1_s ? frc::Timer::GetFPGATimestamp() - m_startTime
: -1_s;
}

View File

@@ -4,6 +4,8 @@
#include "frc2/command/WrapperCommand.h"
#include "frc2/command/Command.h"
using namespace frc2;
WrapperCommand::WrapperCommand(std::unique_ptr<Command>&& command) {
@@ -33,3 +35,11 @@ void WrapperCommand::End(bool interrupted) {
bool WrapperCommand::RunsWhenDisabled() const {
return m_command->RunsWhenDisabled();
}
Command::InterruptionBehavior WrapperCommand::GetInterruptionBehavior() const {
return m_command->GetInterruptionBehavior();
}
wpi::SmallSet<Subsystem*, 4> WrapperCommand::GetRequirements() const {
return m_command->GetRequirements();
}

View File

@@ -8,8 +8,8 @@ using namespace frc2;
Button::Button(std::function<bool()> isPressed) : Trigger(isPressed) {}
Button Button::WhenPressed(Command* command, bool interruptible) {
WhenActive(command, interruptible);
Button Button::WhenPressed(Command* command) {
WhenActive(command);
return *this;
}
@@ -25,8 +25,8 @@ Button Button::WhenPressed(std::function<void()> toRun,
return *this;
}
Button Button::WhileHeld(Command* command, bool interruptible) {
WhileActiveContinous(command, interruptible);
Button Button::WhileHeld(Command* command) {
WhileActiveContinous(command);
return *this;
}
@@ -42,13 +42,13 @@ Button Button::WhileHeld(std::function<void()> toRun,
return *this;
}
Button Button::WhenHeld(Command* command, bool interruptible) {
WhileActiveOnce(command, interruptible);
Button Button::WhenHeld(Command* command) {
WhileActiveOnce(command);
return *this;
}
Button Button::WhenReleased(Command* command, bool interruptible) {
WhenInactive(command, interruptible);
Button Button::WhenReleased(Command* command) {
WhenInactive(command);
return *this;
}
@@ -64,8 +64,8 @@ Button Button::WhenReleased(std::function<void()> toRun,
return *this;
}
Button Button::ToggleWhenPressed(Command* command, bool interruptible) {
ToggleWhenActive(command, interruptible);
Button Button::ToggleWhenPressed(Command* command) {
ToggleWhenActive(command);
return *this;
}

View File

@@ -13,9 +13,8 @@ using namespace frc2;
Trigger::Trigger(const Trigger& other) = default;
Trigger Trigger::WhenActive(Command* command, bool interruptible) {
this->Rising().IfHigh(
[command, interruptible] { command->Schedule(interruptible); });
Trigger Trigger::WhenActive(Command* command) {
this->Rising().IfHigh([command] { command->Schedule(); });
return *this;
}
@@ -30,8 +29,8 @@ Trigger Trigger::WhenActive(std::function<void()> toRun,
return WhenActive(InstantCommand(std::move(toRun), requirements));
}
Trigger Trigger::WhileActiveContinous(Command* command, bool interruptible) {
this->IfHigh([command, interruptible] { command->Schedule(interruptible); });
Trigger Trigger::WhileActiveContinous(Command* command) {
this->IfHigh([command] { command->Schedule(); });
this->Falling().IfHigh([command] { command->Cancel(); });
return *this;
}
@@ -48,16 +47,14 @@ Trigger Trigger::WhileActiveContinous(
return WhileActiveContinous(InstantCommand(std::move(toRun), requirements));
}
Trigger Trigger::WhileActiveOnce(Command* command, bool interruptible) {
this->Rising().IfHigh(
[command, interruptible] { command->Schedule(interruptible); });
Trigger Trigger::WhileActiveOnce(Command* command) {
this->Rising().IfHigh([command] { command->Schedule(); });
this->Falling().IfHigh([command] { command->Cancel(); });
return *this;
}
Trigger Trigger::WhenInactive(Command* command, bool interruptible) {
this->Falling().IfHigh(
[command, interruptible] { command->Schedule(interruptible); });
Trigger Trigger::WhenInactive(Command* command) {
this->Falling().IfHigh([command] { command->Schedule(); });
return *this;
}
@@ -72,12 +69,12 @@ Trigger Trigger::WhenInactive(std::function<void()> toRun,
return WhenInactive(InstantCommand(std::move(toRun), requirements));
}
Trigger Trigger::ToggleWhenActive(Command* command, bool interruptible) {
this->Rising().IfHigh([command, interruptible] {
Trigger Trigger::ToggleWhenActive(Command* command) {
this->Rising().IfHigh([command] {
if (command->IsScheduled()) {
command->Cancel();
} else {
command->Schedule(interruptible);
command->Schedule();
}
});
return *this;

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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();
}