// 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 "frc2/command/Subsystem.h" namespace frc2 { template std::string GetTypeName(const T& type) { return wpi::Demangle(typeid(type).name()); } class PerpetualCommand; /** * A state machine representing a complete action to be performed by the robot. * Commands are run by the CommandScheduler, and can be composed into * CommandGroups to allow users to build complicated multi-step actions without * the need to roll the state machine logic themselves. * *

Commands are run synchronously from the main robot loop; no * multithreading is used, unless specified explicitly from the command * implementation. * *

Note: ALWAYS create a subclass by extending CommandHelper, * or decorators will not function! * * This class is provided by the NewCommands VendorDep * * @see CommandScheduler * @see CommandHelper */ class Command { public: Command() = default; virtual ~Command(); Command(const Command&) = default; Command& operator=(const Command& rhs); Command(Command&&) = default; Command& operator=(Command&&) = default; /** * The initial subroutine of a command. Called once when the command is * initially scheduled. */ virtual void Initialize(); /** * The main body of a command. Called repeatedly while the command is * scheduled. */ virtual void Execute(); /** * The action to take when the command ends. Called when either the command * finishes normally, or when it interrupted/canceled. * * @param interrupted whether the command was interrupted/canceled */ virtual void End(bool interrupted); /** * Whether the command has finished. Once a command finishes, the scheduler * will call its end() method and un-schedule it. * * @return whether the command has finished. */ 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 another command is scheduled * that shares a requirement, GetInterruptionBehavior() will be checked and * followed. If no subsystems are required, return an empty set. * *

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 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. * *

This is the default behavior. */ kCancelSelf, /** This command continues, and the incoming command is not scheduled. */ kCancelIncoming }; friend class CommandPtr; /** * Decorates this command with a timeout. If the specified timeout is * exceeded before the command finishes normally, the command will be * interrupted and un-scheduled. Note that the timeout only applies to the * command returned by this method; the calling command is not itself changed. * * @param duration the timeout duration * @return the command with the timeout added */ [[nodiscard]] CommandPtr WithTimeout(units::second_t duration) &&; /** * Decorates this command with an interrupt condition. If the specified * condition becomes true before the command finishes normally, the command * will be interrupted and un-scheduled. Note that this only applies to the * command returned by this method; the calling command is not itself changed. * * @param condition the interrupt condition * @return the command with the interrupt condition added */ [[nodiscard]] CommandPtr Until(std::function condition) &&; /** * Decorates this command with an interrupt condition. If the specified * condition becomes true before the command finishes normally, the command * will be interrupted and un-scheduled. Note that this only applies to the * command returned by this method; the calling command is not itself changed. * * @param condition the interrupt condition * @return the command with the interrupt condition added * @deprecated Replace with Until() */ WPI_DEPRECATED("Replace with Until()") [[nodiscard]] CommandPtr WithInterrupt(std::function condition) &&; /** * Decorates this command with a runnable to run before this command starts. * * @param toRun the Runnable to run * @param requirements the required subsystems * @return the decorated command */ [[nodiscard]] CommandPtr BeforeStarting( std::function toRun, std::initializer_list requirements) &&; /** * Decorates this command with a runnable to run before this command starts. * * @param toRun the Runnable to run * @param requirements the required subsystems * @return the decorated command */ [[nodiscard]] CommandPtr BeforeStarting( std::function toRun, std::span requirements = {}) &&; /** * Decorates this command with a runnable to run after the command finishes. * * @param toRun the Runnable to run * @param requirements the required subsystems * @return the decorated command */ [[nodiscard]] CommandPtr AndThen( std::function toRun, std::initializer_list requirements) &&; /** * Decorates this command with a runnable to run after the command finishes. * * @param toRun the Runnable to run * @param requirements the required subsystems * @return the decorated command */ [[nodiscard]] CommandPtr AndThen( std::function toRun, std::span requirements = {}) &&; /** * Decorates this command to run perpetually, ignoring its ordinary end * conditions. The decorated command can still be interrupted or canceled. * * @return the decorated command * @deprecated PerpetualCommand violates the assumption that execute() doesn't get called after isFinished() returns true -- an assumption that should be valid. This was unsafe/undefined behavior from the start, and RepeatCommand provides an easy way to achieve similar end results with slightly different (and safe) semantics. */ WPI_DEPRECATED( "PerpetualCommand violates the assumption that execute() doesn't get " "called after isFinished() returns true -- an assumption that should be " "valid." "This was unsafe/undefined behavior from the start, and RepeatCommand " "provides an easy way to achieve similar end results with slightly " "different (and safe) semantics.") PerpetualCommand Perpetually() &&; /** * Decorates this command to run repeatedly, restarting it when it ends, until * this command is interrupted. The decorated command can still be canceled. * * @return the decorated command */ [[nodiscard]] CommandPtr Repeatedly() &&; /** * Decorates this command to run "by proxy" by wrapping it in a * ProxyCommand. This is useful for "forking off" from command groups * when the user does not wish to extend the command's requirements to the * entire command group. * *

This overload transfers command ownership to the returned CommandPtr. * * @return the decorated command */ [[nodiscard]] CommandPtr AsProxy() &&; /** * Decorates this command to only run if this condition is not met. If the * command is already running and the condition changes to true, the command * will not stop running. The requirements of this command will be kept for * the new conditonal command. * * @param condition the condition that will prevent the command from running * @return the decorated command */ [[nodiscard]] CommandPtr Unless(std::function condition) &&; /** * Decorates this command to run or stop when disabled. * * @param doesRunWhenDisabled true to run when disabled. * @return the decorated command */ [[nodiscard]] CommandPtr IgnoringDisable(bool doesRunWhenDisabled) &&; /** * Decorates this command to run or stop when disabled. * * @param interruptBehavior true to run when disabled. * @return the decorated command */ [[nodiscard]] CommandPtr WithInterruptBehavior( Command::InterruptionBehavior interruptBehavior) &&; /** * Decorates this command with a lambda to call on interrupt or end, following * the command's inherent Command::End(bool) method. * * @param end a lambda accepting a boolean parameter specifying whether the * command was interrupted. * @return the decorated command */ [[nodiscard]] CommandPtr FinallyDo(std::function end) &&; /** * Decorates this command with a lambda to call on interrupt, following the * command's inherent Command::End(bool) method. * * @param handler a lambda to run when the command is interrupted * @return the decorated command */ [[nodiscard]] CommandPtr HandleInterrupt(std::function handler) &&; /** * Decorates this Command with a name. * * @param name name * @return the decorated Command */ [[nodiscard]] CommandPtr WithName(std::string_view name) &&; /** * Schedules this command. */ void Schedule(); /** * Cancels this command. Will call End(true). Commands will be canceled * regardless of interruption behavior. */ void Cancel(); /** * Whether or not the command is currently scheduled. Note that this does not * detect whether the command is in a composition, only whether it is directly * being run by the scheduler. * * @return Whether the command is scheduled. */ bool IsScheduled() const; /** * Whether the command requires a given subsystem. Named "HasRequirement" * rather than "requires" to avoid confusion with Command::Requires(Subsystem) * -- this may be able to be changed in a few years. * * @param requirement the subsystem to inquire about * @return whether the subsystem is required */ bool HasRequirement(Subsystem* requirement) const; /** * Whether the command is currently grouped in a command group. Used as extra * insurance to prevent accidental independent use of grouped commands. */ bool IsComposed() const; /** * Sets whether the command is currently composed in a command composition. * Can be used to "reclaim" a command if a composition is no longer going to * use it. NOT ADVISED! */ void SetComposed(bool isComposed); /** * Whether the command is currently grouped in a command group. Used as extra * insurance to prevent accidental independent use of grouped commands. * * @deprecated Moved to IsComposed() */ WPI_DEPRECATED("Moved to IsComposed()") bool IsGrouped() const; /** * Sets whether the command is currently grouped in a command group. Can be * used to "reclaim" a command if a group is no longer going to use it. NOT * ADVISED! * * @deprecated Moved to SetComposed() */ WPI_DEPRECATED("Moved to SetComposed()") void SetGrouped(bool grouped); /** * Whether the given command should run when the robot is disabled. Override * to return true if the command should run when disabled. * * @return whether the command should run when the robot is disabled */ 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; } /** * Gets the name of this Command. Defaults to the simple class name if not * overridden. * * @return The display name of the Command */ virtual std::string GetName() const; /** * Sets the name of this Command. Nullop if not overridden. * * @param name The display name of the Command. */ virtual void SetName(std::string_view name); /** * Transfers ownership of this command to a unique pointer. Used for * decorator methods. */ virtual CommandPtr ToPtr() && = 0; protected: /** * Transfers ownership of this command to a unique pointer. Used for * decorator methods. */ virtual std::unique_ptr TransferOwnership() && = 0; bool m_isComposed = false; }; /** * Checks if two commands have disjoint requirement sets. * * @param first The first command to check. * @param second The second command to check. * @return False if first and second share a requirement. */ bool RequirementsDisjoint(Command* first, Command* second); } // namespace frc2