[commands] Move GroupedCommands to CommandScheduler (#4728)

Move the command group checking functionality from CommandGroupBase into CommandScheduler.
Update references to grouping as composition for clarity (because explicitly grouping isn't the only way to do it).
Deprecate the static factory methods parallel, race, and deadline in CommandGroupBase in favor of the identical ones in Commands.
This commit is contained in:
Starlight220
2022-12-07 07:13:31 +02:00
committed by GitHub
parent f18fd41ac3
commit 4bbdbdfb48
46 changed files with 450 additions and 472 deletions

View File

@@ -67,11 +67,11 @@ public interface Command {
* finishes normally, the command will be interrupted and un-scheduled. Note that the timeout only * 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. * applies to the command returned by this method; the calling command is not itself changed.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param seconds the timeout duration * @param seconds the timeout duration
* @return the command with the timeout added * @return the command with the timeout added
@@ -86,11 +86,11 @@ public interface Command {
* that this only applies to the command returned by this method; the calling command is not * that this only applies to the command returned by this method; the calling command is not
* itself changed. * itself changed.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param condition the interrupt condition * @param condition the interrupt condition
* @return the command with the interrupt condition added * @return the command with the interrupt condition added
@@ -105,11 +105,11 @@ public interface Command {
* that this only applies to the command returned by this method; the calling command is not * that this only applies to the command returned by this method; the calling command is not
* itself changed. * itself changed.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param condition the interrupt condition * @param condition the interrupt condition
* @return the command with the interrupt condition added * @return the command with the interrupt condition added
@@ -123,11 +123,11 @@ public interface Command {
/** /**
* Decorates this command with a runnable to run before this command starts. * Decorates this command with a runnable to run before this command starts.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param toRun the Runnable to run * @param toRun the Runnable to run
* @param requirements the required subsystems * @param requirements the required subsystems
@@ -140,11 +140,11 @@ public interface Command {
/** /**
* Decorates this command with another command to run before this command starts. * Decorates this command with another command to run before this command starts.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param before the command to run before this one * @param before the command to run before this one
* @return the decorated command * @return the decorated command
@@ -156,11 +156,11 @@ public interface Command {
/** /**
* Decorates this command with a runnable to run after the command finishes. * Decorates this command with a runnable to run after the command finishes.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param toRun the Runnable to run * @param toRun the Runnable to run
* @param requirements the required subsystems * @param requirements the required subsystems
@@ -174,11 +174,11 @@ public interface Command {
* Decorates this command with a set of commands to run after it in sequence. Often more * Decorates this command with a set of commands to run after it in sequence. Often more
* convenient/less-verbose than constructing a new {@link SequentialCommandGroup} explicitly. * convenient/less-verbose than constructing a new {@link SequentialCommandGroup} explicitly.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param next the commands to run next * @param next the commands to run next
* @return the decorated command * @return the decorated command
@@ -194,11 +194,11 @@ public interface Command {
* command ends and interrupting all the others. Often more convenient/less-verbose than * command ends and interrupting all the others. Often more convenient/less-verbose than
* constructing a new {@link ParallelDeadlineGroup} explicitly. * constructing a new {@link ParallelDeadlineGroup} explicitly.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param parallel the commands to run in parallel * @param parallel the commands to run in parallel
* @return the decorated command * @return the decorated command
@@ -212,11 +212,11 @@ public interface Command {
* command ends. Often more convenient/less-verbose than constructing a new {@link * command ends. Often more convenient/less-verbose than constructing a new {@link
* ParallelCommandGroup} explicitly. * ParallelCommandGroup} explicitly.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param parallel the commands to run in parallel * @param parallel the commands to run in parallel
* @return the decorated command * @return the decorated command
@@ -232,11 +232,11 @@ public interface Command {
* command ends. Often more convenient/less-verbose than constructing a new {@link * command ends. Often more convenient/less-verbose than constructing a new {@link
* ParallelRaceGroup} explicitly. * ParallelRaceGroup} explicitly.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @param parallel the commands to run in parallel * @param parallel the commands to run in parallel
* @return the decorated command * @return the decorated command
@@ -251,11 +251,11 @@ public interface Command {
* Decorates this command to run perpetually, ignoring its ordinary end conditions. The decorated * Decorates this command to run perpetually, ignoring its ordinary end conditions. The decorated
* command can still be interrupted or canceled. * command can still be interrupted or canceled.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @return the decorated command * @return the decorated command
* @deprecated PerpetualCommand violates the assumption that execute() doesn't get called after * @deprecated PerpetualCommand violates the assumption that execute() doesn't get called after
@@ -273,11 +273,11 @@ public interface Command {
* Decorates this command to run repeatedly, restarting it when it ends, until this command is * Decorates this command to run repeatedly, restarting it when it ends, until this command is
* interrupted. The decorated command can still be canceled. * interrupted. The decorated command can still be canceled.
* *
* <p>Note: This decorator works by composing this command within a CommandGroup. The command * <p>Note: This decorator works by adding this command to a composition. The command the
* cannot be used independently after being decorated, or be re-decorated with a different * decorator was called on cannot be scheduled independently or be added to a different
* decorator, unless it is manually cleared from the list of grouped commands with {@link * composition (namely, decorators), unless it is manually cleared from the list of composed
* CommandGroupBase#clearGroupedCommand(Command)}. The decorated command can, however, be further * commands with {@link CommandScheduler#removeComposedCommand(Command)}. The command composition
* decorated without issue. * returned from this method can be further decorated without issue.
* *
* @return the decorated command * @return the decorated command
*/ */
@@ -287,8 +287,8 @@ public interface Command {
/** /**
* Decorates this command to run "by proxy" by wrapping it in a {@link ProxyCommand}. This is * Decorates this command to run "by proxy" by wrapping it in a {@link ProxyCommand}. This is
* useful for "forking off" from command groups when the user does not wish to extend the * useful for "forking off" from command compositions when the user does not wish to extend the
* command's requirements to the entire command group. * command's requirements to the entire command composition.
* *
* @return the decorated command * @return the decorated command
*/ */
@@ -391,7 +391,7 @@ public interface Command {
/** /**
* Whether or not the command is currently scheduled. Note that this does not detect whether the * Whether or not the command is currently scheduled. Note that this does not detect whether the
* command is being run by a CommandGroup, only whether it is directly being run by the scheduler. * command is in a composition, only whether it is directly being run by the scheduler.
* *
* @return Whether the command is scheduled. * @return Whether the command is scheduled.
*/ */

View File

@@ -104,6 +104,6 @@ public abstract class CommandBase implements Sendable, Command {
} }
}); });
builder.addBooleanProperty( builder.addBooleanProperty(
".isParented", () -> CommandGroupBase.getGroupedCommands().contains(this), null); ".isParented", () -> CommandScheduler.getInstance().isComposed(this), null);
} }
} }

View File

@@ -4,75 +4,15 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
/** /**
* A base for CommandGroups. Statically tracks commands that have been allocated to groups to ensure * A base for CommandGroups.
* those commands are not also used independently, which can result in inconsistent command state
* and unpredictable execution.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*
* @deprecated This class is an empty abstraction. Inherit directly from CommandBase/Command.
*/ */
@Deprecated(forRemoval = true)
public abstract class CommandGroupBase extends CommandBase { public abstract class CommandGroupBase extends CommandBase {
private static final Set<Command> m_groupedCommands =
Collections.newSetFromMap(new WeakHashMap<>());
static void registerGroupedCommands(Command... commands) {
m_groupedCommands.addAll(Set.of(commands));
}
/**
* Clears the list of grouped commands, allowing all commands to be freely used again.
*
* <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
* this unless you fully understand what you are doing.
*/
public static void clearGroupedCommands() {
m_groupedCommands.clear();
}
/**
* Removes a single command from the list of grouped commands, allowing it to be freely used
* again.
*
* <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
* this unless you fully understand what you are doing.
*
* @param command the command to remove from the list of grouped commands
*/
public static void clearGroupedCommand(Command command) {
m_groupedCommands.remove(command);
}
/**
* Requires that the specified commands not have been already allocated to a CommandGroup. Throws
* an {@link IllegalArgumentException} if commands have been allocated.
*
* @param commands The commands to check
*/
public static void requireUngrouped(Command... commands) {
requireUngrouped(Set.of(commands));
}
/**
* Requires that the specified commands not have been already allocated to a CommandGroup. Throws
* an {@link IllegalArgumentException} if commands have been allocated.
*
* @param commands The commands to check
*/
public static void requireUngrouped(Collection<Command> commands) {
if (!Collections.disjoint(commands, getGroupedCommands())) {
throw new IllegalArgumentException("Commands cannot be added to more than one CommandGroup");
}
}
static Set<Command> getGroupedCommands() {
return m_groupedCommands;
}
/** /**
* Adds the given commands to the command group. * Adds the given commands to the command group.
* *
@@ -85,8 +25,10 @@ public abstract class CommandGroupBase extends CommandBase {
* *
* @param commands the commands to include * @param commands the commands to include
* @return the command group * @return the command group
* @deprecated Replace with {@link Commands#sequence(Command...)}
*/ */
public static CommandGroupBase sequence(Command... commands) { @Deprecated
public static SequentialCommandGroup sequence(Command... commands) {
return new SequentialCommandGroup(commands); return new SequentialCommandGroup(commands);
} }
@@ -95,8 +37,10 @@ public abstract class CommandGroupBase extends CommandBase {
* *
* @param commands the commands to include * @param commands the commands to include
* @return the command group * @return the command group
* @deprecated Replace with {@link Commands#parallel(Command...)}
*/ */
public static CommandGroupBase parallel(Command... commands) { @Deprecated
public static ParallelCommandGroup parallel(Command... commands) {
return new ParallelCommandGroup(commands); return new ParallelCommandGroup(commands);
} }
@@ -105,8 +49,10 @@ public abstract class CommandGroupBase extends CommandBase {
* *
* @param commands the commands to include * @param commands the commands to include
* @return the command group * @return the command group
* @deprecated Replace with {@link Commands#race(Command...)}
*/ */
public static CommandGroupBase race(Command... commands) { @Deprecated
public static ParallelRaceGroup race(Command... commands) {
return new ParallelRaceGroup(commands); return new ParallelRaceGroup(commands);
} }
@@ -116,8 +62,10 @@ public abstract class CommandGroupBase extends CommandBase {
* @param deadline the deadline command * @param deadline the deadline command
* @param commands the commands to include * @param commands the commands to include
* @return the command group * @return the command group
* @deprecated Replace with {@link Commands#deadline(Command, Command...)}
*/ */
public static CommandGroupBase deadline(Command deadline, Command... commands) { @Deprecated
public static ParallelDeadlineGroup deadline(Command deadline, Command... commands) {
return new ParallelDeadlineGroup(deadline, commands); return new ParallelDeadlineGroup(deadline, commands);
} }
} }

View File

@@ -26,6 +26,7 @@ import edu.wpi.first.wpilibj.event.EventLoop;
import edu.wpi.first.wpilibj.livewindow.LiveWindow; import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior; import edu.wpi.first.wpilibj2.command.Command.InterruptionBehavior;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -33,6 +34,7 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -60,6 +62,8 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
return instance; return instance;
} }
private final Set<Command> m_composedCommands = Collections.newSetFromMap(new WeakHashMap<>());
// A set of the currently-running commands. // A set of the currently-running commands.
private final Set<Command> m_scheduledCommands = new LinkedHashSet<>(); private final Set<Command> m_scheduledCommands = new LinkedHashSet<>();
@@ -208,10 +212,7 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
return; return;
} }
if (CommandGroupBase.getGroupedCommands().contains(command)) { requireNotComposed(command);
throw new IllegalArgumentException(
"A command that is part of a command group cannot be independently scheduled");
}
// Do nothing if the scheduler is disabled, the robot is disabled and the command doesn't // Do nothing if the scheduler is disabled, the robot is disabled and the command doesn't
// run when disabled, or the command is already scheduled. // run when disabled, or the command is already scheduled.
@@ -405,6 +406,8 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
return; return;
} }
requireNotComposed(defaultCommand);
if (!defaultCommand.getRequirements().contains(subsystem)) { if (!defaultCommand.getRequirements().contains(subsystem)) {
throw new IllegalArgumentException("Default commands must require their subsystem!"); throw new IllegalArgumentException("Default commands must require their subsystem!");
} }
@@ -489,8 +492,8 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
/** /**
* Whether the given commands are running. Note that this only works on commands that are directly * 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 commands inside of CommandGroups, as the * scheduled by the scheduler; it will not work on commands inside compositions, as the scheduler
* scheduler does not see them. * does not see them.
* *
* @param commands the command to query * @param commands the command to query
* @return whether the command is currently scheduled * @return whether the command is currently scheduled
@@ -557,6 +560,85 @@ public final class CommandScheduler implements NTSendable, AutoCloseable {
m_finishActions.add(requireNonNullParam(action, "action", "onCommandFinish")); m_finishActions.add(requireNonNullParam(action, "action", "onCommandFinish"));
} }
/**
* Register commands as composed. An exception will be thrown if these commands are scheduled
* directly or added to a composition.
*
* @param commands the commands to register
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public void registerComposedCommands(Command... commands) {
var commandSet = Set.of(commands);
requireNotComposed(commandSet);
m_composedCommands.addAll(commandSet);
}
/**
* Clears the list of composed commands, allowing all commands to be freely used again.
*
* <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
* this unless you fully understand what you are doing.
*/
public void clearComposedCommands() {
m_composedCommands.clear();
}
/**
* Removes a single command from the list of composed commands, allowing it to be freely used
* again.
*
* <p>WARNING: Using this haphazardly can result in unexpected/undesirable behavior. Do not use
* this unless you fully understand what you are doing.
*
* @param command the command to remove from the list of grouped commands
*/
public void removeComposedCommand(Command command) {
m_composedCommands.remove(command);
}
/**
* Requires that the specified command hasn't been already added to a composition.
*
* @param command The command to check
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public void requireNotComposed(Command command) {
if (m_composedCommands.contains(command)) {
throw new IllegalArgumentException(
"Commands that have been composed may not be added to another composition or scheduled"
+ "individually!");
}
}
/**
* Requires that the specified commands not have been already added to a composition.
*
* @param commands The commands to check
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public void requireNotComposed(Collection<Command> commands) {
if (!Collections.disjoint(commands, getComposedCommands())) {
throw new IllegalArgumentException(
"Commands that have been composed may not be added to another composition or scheduled"
+ "individually!");
}
}
/**
* Check if the given command has been composed.
*
* @param command The command to check
* @return true if composed
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public boolean isComposed(Command command) {
return getComposedCommands().contains(command);
}
Set<Command> getComposedCommands() {
return m_composedCommands;
}
@Override @Override
public void initSendable(NTSendableBuilder builder) { public void initSendable(NTSendableBuilder builder) {
builder.setSmartDashboardType("Scheduler"); builder.setSmartDashboardType("Scheduler");

View File

@@ -5,22 +5,16 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
/** /**
* Runs one of two commands, depending on the value of the given condition when this command is * A command composition that runs one of two commands, depending on the value of the given
* initialized. Does not actually schedule the selected command - rather, the command is run through * condition when this command is initialized.
* this command; this ensures that the command will behave as expected if used as part of a
* CommandGroup. Requires the requirements of both commands, again to ensure proper functioning when
* used in a CommandGroup. If this is undesired, consider using {@link ScheduleCommand}.
* *
* <p>As this command contains multiple component commands within it, it is technically a command * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* group; the command instances that are passed to it cannot be added to any other groups, or * added to any other composition or scheduled individually, and the composition requires all
* scheduled individually. * subsystems its components require.
*
* <p>As a rule, CommandGroups require the union of the requirements of their component commands.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@@ -38,13 +32,12 @@ public class ConditionalCommand extends CommandBase {
* @param condition the condition to determine which command to run * @param condition the condition to determine which command to run
*/ */
public ConditionalCommand(Command onTrue, Command onFalse, BooleanSupplier condition) { public ConditionalCommand(Command onTrue, Command onFalse, BooleanSupplier condition) {
requireUngrouped(onTrue, onFalse); m_onTrue = requireNonNullParam(onTrue, "onTrue", "ConditionalCommand");
m_onFalse = requireNonNullParam(onFalse, "onFalse", "ConditionalCommand");
CommandGroupBase.registerGroupedCommands(onTrue, onFalse);
m_onTrue = onTrue;
m_onFalse = onFalse;
m_condition = requireNonNullParam(condition, "condition", "ConditionalCommand"); m_condition = requireNonNullParam(condition, "condition", "ConditionalCommand");
CommandScheduler.getInstance().registerComposedCommands(onTrue, onFalse);
m_requirements.addAll(m_onTrue.getRequirements()); m_requirements.addAll(m_onTrue.getRequirements());
m_requirements.addAll(m_onFalse.getRequirements()); m_requirements.addAll(m_onFalse.getRequirements());
} }

View File

@@ -9,24 +9,27 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* A CommandGroup that runs a set of commands in parallel, ending when the last command ends. * A command composition that runs a set of commands in parallel, ending when the last command ends.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their component commands. * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* added to any other composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@SuppressWarnings("removal")
public class ParallelCommandGroup extends CommandGroupBase { public class ParallelCommandGroup extends CommandGroupBase {
// maps commands in this group to whether they are still running // maps commands in this composition to whether they are still running
private final Map<Command, Boolean> m_commands = new HashMap<>(); private final Map<Command, Boolean> m_commands = new HashMap<>();
private boolean m_runWhenDisabled = true; private boolean m_runWhenDisabled = true;
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming; private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
/** /**
* Creates a new ParallelCommandGroup. The given commands will be executed simultaneously. The * Creates a new ParallelCommandGroup. The given commands will be executed simultaneously. The
* command group will finish when the last command finishes. If the CommandGroup is interrupted, * command composition will finish when the last command finishes. If the composition is
* only the commands that are still running will be interrupted. * interrupted, only the commands that are still running will be interrupted.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
public ParallelCommandGroup(Command... commands) { public ParallelCommandGroup(Command... commands) {
addCommands(commands); addCommands(commands);
@@ -34,19 +37,17 @@ public class ParallelCommandGroup extends CommandGroupBase {
@Override @Override
public final void addCommands(Command... commands) { public final void addCommands(Command... commands) {
requireUngrouped(commands);
if (m_commands.containsValue(true)) { if (m_commands.containsValue(true)) {
throw new IllegalStateException( throw new IllegalStateException(
"Commands cannot be added to a CommandGroup while the group is running"); "Commands cannot be added to a composition while it's running");
} }
registerGroupedCommands(commands); CommandScheduler.getInstance().registerComposedCommands(commands);
for (Command command : commands) { for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), m_requirements)) { if (!Collections.disjoint(command.getRequirements(), m_requirements)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Multiple commands in a parallel group cannot" + "require the same subsystems"); "Multiple commands in a parallel composition cannot require the same subsystems");
} }
m_commands.put(command, false); m_commands.put(command, false);
m_requirements.addAll(command.getRequirements()); m_requirements.addAll(command.getRequirements());

View File

@@ -9,15 +9,19 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* A CommandGroup that runs a set of commands in parallel, ending only when a specific command (the * A command composition that runs a set of commands in parallel, ending only when a specific
* "deadline") ends, interrupting all other commands that are still running at that point. * command (the "deadline") ends, interrupting all other commands that are still running at that
* point.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their component commands. * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* added to any other composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@SuppressWarnings("removal")
public class ParallelDeadlineGroup extends CommandGroupBase { public class ParallelDeadlineGroup extends CommandGroupBase {
// maps commands in this group to whether they are still running // maps commands in this composition to whether they are still running
private final Map<Command, Boolean> m_commands = new HashMap<>(); private final Map<Command, Boolean> m_commands = new HashMap<>();
private boolean m_runWhenDisabled = true; private boolean m_runWhenDisabled = true;
private boolean m_finished = true; private boolean m_finished = true;
@@ -26,11 +30,11 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
/** /**
* Creates a new ParallelDeadlineGroup. The given commands (including the deadline) will be * Creates a new ParallelDeadlineGroup. The given commands (including the deadline) will be
* executed simultaneously. The CommandGroup will finish when the deadline finishes, interrupting * executed simultaneously. The composition will finish when the deadline finishes, interrupting
* all other still-running commands. If the CommandGroup is interrupted, only the commands still * all other still-running commands. If the composition is interrupted, only the commands still
* running will be interrupted. * running will be interrupted.
* *
* @param deadline the command that determines when the group ends * @param deadline the command that determines when the composition ends
* @param commands the commands to be executed * @param commands the commands to be executed
*/ */
public ParallelDeadlineGroup(Command deadline, Command... commands) { public ParallelDeadlineGroup(Command deadline, Command... commands) {
@@ -56,14 +60,12 @@ public class ParallelDeadlineGroup extends CommandGroupBase {
@Override @Override
public final void addCommands(Command... commands) { public final void addCommands(Command... commands) {
requireUngrouped(commands);
if (!m_finished) { if (!m_finished) {
throw new IllegalStateException( throw new IllegalStateException(
"Commands cannot be added to a CommandGroup while the group is running"); "Commands cannot be added to a composition while it's running");
} }
registerGroupedCommands(commands); CommandScheduler.getInstance().registerComposedCommands(commands);
for (Command command : commands) { for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), m_requirements)) { if (!Collections.disjoint(command.getRequirements(), m_requirements)) {

View File

@@ -9,13 +9,16 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
* A CommandGroup that runs a set of commands in parallel, ending when any one of the commands ends * A composition that runs a set of commands in parallel, ending when any one of the commands ends
* and interrupting all the others. * and interrupting all the others.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their component commands. * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* added to any other composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@SuppressWarnings("removal")
public class ParallelRaceGroup extends CommandGroupBase { public class ParallelRaceGroup extends CommandGroupBase {
private final Set<Command> m_commands = new HashSet<>(); private final Set<Command> m_commands = new HashSet<>();
private boolean m_runWhenDisabled = true; private boolean m_runWhenDisabled = true;
@@ -27,7 +30,7 @@ public class ParallelRaceGroup extends CommandGroupBase {
* "race to the finish" - the first command to finish ends the entire command, with all other * "race to the finish" - the first command to finish ends the entire command, with all other
* commands being interrupted. * commands being interrupted.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
public ParallelRaceGroup(Command... commands) { public ParallelRaceGroup(Command... commands) {
addCommands(commands); addCommands(commands);
@@ -35,19 +38,17 @@ public class ParallelRaceGroup extends CommandGroupBase {
@Override @Override
public final void addCommands(Command... commands) { public final void addCommands(Command... commands) {
requireUngrouped(commands);
if (!m_finished) { if (!m_finished) {
throw new IllegalStateException( throw new IllegalStateException(
"Commands cannot be added to a CommandGroup while the group is running"); "Commands cannot be added to a composition while it's running!");
} }
registerGroupedCommands(commands); CommandScheduler.getInstance().registerComposedCommands(commands);
for (Command command : commands) { for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), m_requirements)) { if (!Collections.disjoint(command.getRequirements(), m_requirements)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Multiple commands in a parallel group cannot" + " require the same subsystems"); "Multiple commands in a parallel composition cannot require the same subsystems");
} }
m_commands.add(command); m_commands.add(command);
m_requirements.addAll(command.getRequirements()); m_requirements.addAll(command.getRequirements());

View File

@@ -4,12 +4,9 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.registerGroupedCommands;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
/** /**
* A command that runs another command in perpetuity, ignoring that command's end conditions. While * A command that runs another command in perpetuity, ignoring that command's end conditions. While
* this class does not extend {@link CommandGroupBase}, it is still considered a CommandGroup, as it * this class does not extend {@link CommandGroupBase}, it is still considered a composition, as it
* allows one to compose another command within it; the command instances that are passed to it * allows one to compose another command within it; the command instances that are passed to it
* cannot be added to any other groups, or scheduled individually. * cannot be added to any other groups, or scheduled individually.
* *
@@ -33,8 +30,7 @@ public class PerpetualCommand extends CommandBase {
* @param command the command to run perpetually * @param command the command to run perpetually
*/ */
public PerpetualCommand(Command command) { public PerpetualCommand(Command command) {
requireUngrouped(command); CommandScheduler.getInstance().registerComposedCommands(command);
registerGroupedCommands(command);
m_command = command; m_command = command;
m_requirements.addAll(command.getRequirements()); m_requirements.addAll(command.getRequirements());
} }

View File

@@ -4,16 +4,16 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.registerGroupedCommands; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
/** /**
* A command that runs another command repeatedly, restarting it when it ends, until this command is * A command that runs another command repeatedly, restarting it when it ends, until this command is
* interrupted. While this class does not extend {@link CommandGroupBase}, it is still considered a * interrupted. Command instances that are passed to it cannot be added to any other groups, or
* CommandGroup, as it allows one to compose another command within it; the command instances that * scheduled individually.
* are passed to it cannot be added to any other groups, or scheduled individually.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their component commands. * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* added to any other composition or scheduled individually,and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@@ -28,9 +28,8 @@ public class RepeatCommand extends CommandBase {
* @param command the command to run repeatedly * @param command the command to run repeatedly
*/ */
public RepeatCommand(Command command) { public RepeatCommand(Command command) {
requireUngrouped(command); m_command = requireNonNullParam(command, "command", "RepeatCommand");
registerGroupedCommands(command); CommandScheduler.getInstance().registerComposedCommands(command);
m_command = command;
m_requirements.addAll(command.getRequirements()); m_requirements.addAll(command.getRequirements());
} }

View File

@@ -8,8 +8,8 @@ import java.util.Set;
/** /**
* Schedules the given commands when this command is initialized. Useful for forking off from * Schedules the given commands when this command is initialized. Useful for forking off from
* CommandGroups. Note that if run from a CommandGroup, the group will not know about the status of * CommandGroups. Note that if run from a composition, the composition will not know about the
* the scheduled commands, and will treat this command as finishing instantly. * status of the scheduled commands, and will treat this command as finishing instantly.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */

View File

@@ -5,24 +5,17 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam; import static edu.wpi.first.wpilibj.util.ErrorMessages.requireNonNullParam;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* Runs one of a selection of commands, either using a selector and a key to command mapping, or a * A command composition that runs one of a selection of commands, either using a selector and a key
* supplier that returns the command directly at runtime. Does not actually schedule the selected * to command mapping, or a supplier that returns the command directly at runtime.
* command - rather, the command is run through this command; this ensures that the command will
* behave as expected if used as part of a CommandGroup. Requires the requirements of all included
* commands, again to ensure proper functioning when used in a CommandGroup. If this is undesired,
* consider using {@link ScheduleCommand}.
* *
* <p>As this command contains multiple component commands within it, it is technically a command * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* group; the command instances that are passed to it cannot be added to any other groups, or * added to any other composition or scheduled individually, and the composition requires all
* scheduled individually. * subsystems its components require.
*
* <p>As a rule, CommandGroups require the union of the requirements of their component commands.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@@ -41,13 +34,12 @@ public class SelectCommand extends CommandBase {
* @param selector the selector to determine which command to run * @param selector the selector to determine which command to run
*/ */
public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) { public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) {
requireUngrouped(commands.values());
CommandGroupBase.registerGroupedCommands(commands.values().toArray(new Command[] {}));
m_commands = requireNonNullParam(commands, "commands", "SelectCommand"); m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
m_selector = requireNonNullParam(selector, "selector", "SelectCommand"); m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
CommandScheduler.getInstance()
.registerComposedCommands(commands.values().toArray(new Command[] {}));
m_toRun = null; m_toRun = null;
for (Command command : m_commands.values()) { for (Command command : m_commands.values()) {

View File

@@ -8,12 +8,15 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A CommandGroups that runs a list of commands in sequence. * A command composition that runs a list of commands in sequence.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their component commands. * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* added to any other composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */
@SuppressWarnings("removal")
public class SequentialCommandGroup extends CommandGroupBase { public class SequentialCommandGroup extends CommandGroupBase {
private final List<Command> m_commands = new ArrayList<>(); private final List<Command> m_commands = new ArrayList<>();
private int m_currentCommandIndex = -1; private int m_currentCommandIndex = -1;
@@ -22,9 +25,9 @@ public class SequentialCommandGroup extends CommandGroupBase {
/** /**
* Creates a new SequentialCommandGroup. The given commands will be run sequentially, with the * Creates a new SequentialCommandGroup. The given commands will be run sequentially, with the
* CommandGroup finishing when the last command finishes. * composition finishing when the last command finishes.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
public SequentialCommandGroup(Command... commands) { public SequentialCommandGroup(Command... commands) {
addCommands(commands); addCommands(commands);
@@ -32,14 +35,12 @@ public class SequentialCommandGroup extends CommandGroupBase {
@Override @Override
public final void addCommands(Command... commands) { public final void addCommands(Command... commands) {
requireUngrouped(commands);
if (m_currentCommandIndex != -1) { if (m_currentCommandIndex != -1) {
throw new IllegalStateException( throw new IllegalStateException(
"Commands cannot be added to a CommandGroup while the group is running"); "Commands cannot be added to a composition while it's running");
} }
registerGroupedCommands(commands); CommandScheduler.getInstance().registerComposedCommands(commands);
for (Command command : commands) { for (Command command : commands) {
m_commands.add(command); m_commands.add(command);

View File

@@ -4,17 +4,15 @@
package edu.wpi.first.wpilibj2.command; package edu.wpi.first.wpilibj2.command;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.registerGroupedCommands;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
import java.util.Set; import java.util.Set;
/** /**
* A class used internally to wrap commands while overriding a specific method; all other methods * A class used internally to wrap commands while overriding a specific method; all other methods
* will call through to the wrapped command. * will call through to the wrapped command.
* *
* <p>Wrapped commands may only be used through the wrapper, trying to directly schedule them or add * <p>The rules for command compositions apply: command instances that are passed to it cannot be
* them to a group will throw an exception. * added to any other composition or scheduled individually, and the composition requires all
* subsystems its components require.
*/ */
public abstract class WrapperCommand extends CommandBase { public abstract class WrapperCommand extends CommandBase {
protected final Command m_command; protected final Command m_command;
@@ -23,11 +21,10 @@ public abstract class WrapperCommand extends CommandBase {
* Wrap a command. * Wrap a command.
* *
* @param command the command being wrapped. Trying to directly schedule this command or add it to * @param command the command being wrapped. Trying to directly schedule this command or add it to
* a group will throw an exception. * a composition will throw an exception.
*/ */
protected WrapperCommand(Command command) { protected WrapperCommand(Command command) {
requireUngrouped(command); CommandScheduler.getInstance().registerComposedCommands(command);
registerGroupedCommands(command);
m_command = command; m_command = command;
} }

View File

@@ -25,7 +25,7 @@ Command::~Command() {
} }
Command& Command::operator=(const Command& rhs) { Command& Command::operator=(const Command& rhs) {
m_isGrouped = false; m_isComposed = false;
return *this; return *this;
} }
@@ -135,12 +135,20 @@ std::string Command::GetName() const {
void Command::SetName(std::string_view name) {} void Command::SetName(std::string_view name) {}
bool Command::IsComposed() const {
return m_isComposed;
}
void Command::SetComposed(bool isComposed) {
m_isComposed = isComposed;
}
bool Command::IsGrouped() const { bool Command::IsGrouped() const {
return m_isGrouped; return IsComposed();
} }
void Command::SetGrouped(bool grouped) { void Command::SetGrouped(bool grouped) {
m_isGrouped = grouped; SetComposed(grouped);
} }
namespace frc2 { namespace frc2 {

View File

@@ -5,44 +5,3 @@
#include "frc2/command/CommandGroupBase.h" #include "frc2/command/CommandGroupBase.h"
using namespace frc2; using namespace frc2;
bool CommandGroupBase::RequireUngrouped(const Command& command) {
if (command.IsGrouped()) {
throw FRC_MakeError(
frc::err::CommandIllegalUse,
"Commands cannot be added to more than one CommandGroup");
}
return true;
}
bool CommandGroupBase::RequireUngrouped(const Command* command) {
return RequireUngrouped(*command);
}
bool CommandGroupBase::RequireUngrouped(
std::span<const std::unique_ptr<Command>> commands) {
bool allUngrouped = true;
for (auto&& command : commands) {
allUngrouped &= !command.get()->IsGrouped();
}
if (!allUngrouped) {
throw FRC_MakeError(
frc::err::CommandIllegalUse,
"Commands cannot be added to more than one CommandGroup");
}
return allUngrouped;
}
bool CommandGroupBase::RequireUngrouped(
std::initializer_list<const Command*> commands) {
bool allUngrouped = true;
for (auto&& command : commands) {
allUngrouped &= !command->IsGrouped();
}
if (!allUngrouped) {
throw FRC_MakeError(
frc::err::CommandIllegalUse,
"Commands cannot be added to more than one CommandGroup");
}
return allUngrouped;
}

View File

@@ -19,7 +19,6 @@
#include <wpi/SmallVector.h> #include <wpi/SmallVector.h>
#include <wpi/sendable/SendableRegistry.h> #include <wpi/sendable/SendableRegistry.h>
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandPtr.h" #include "frc2/command/CommandPtr.h"
#include "frc2/command/Subsystem.h" #include "frc2/command/Subsystem.h"
@@ -118,12 +117,8 @@ void CommandScheduler::Schedule(Command* command) {
return; return;
} }
if (command->IsGrouped()) { RequireUngrouped(command);
throw FRC_MakeError(frc::err::CommandIllegalUse,
"A command that is part of a command group "
"cannot be independently scheduled");
return;
}
if (m_impl->disabled || if (m_impl->disabled ||
(frc::RobotState::IsDisabled() && !command->RunsWhenDisabled()) || (frc::RobotState::IsDisabled() && !command->RunsWhenDisabled()) ||
m_impl->scheduledCommands.contains(command)) { m_impl->scheduledCommands.contains(command)) {
@@ -307,6 +302,7 @@ void CommandScheduler::SetDefaultCommand(Subsystem* subsystem,
throw FRC_MakeError(frc::err::CommandIllegalUse, "{}", throw FRC_MakeError(frc::err::CommandIllegalUse, "{}",
"Default commands must require their subsystem!"); "Default commands must require their subsystem!");
} }
RequireUngrouped(defaultCommand.get());
SetDefaultCommandImpl(subsystem, std::move(defaultCommand).Unwrap()); SetDefaultCommandImpl(subsystem, std::move(defaultCommand).Unwrap());
} }
@@ -436,6 +432,28 @@ void CommandScheduler::OnCommandFinish(Action action) {
m_impl->finishActions.emplace_back(std::move(action)); m_impl->finishActions.emplace_back(std::move(action));
} }
void CommandScheduler::RequireUngrouped(const Command* command) {
if (command->IsComposed()) {
throw FRC_MakeError(
frc::err::CommandIllegalUse,
"Commands cannot be added to more than one CommandGroup");
}
}
void CommandScheduler::RequireUngrouped(
std::span<const std::unique_ptr<Command>> commands) {
for (auto&& command : commands) {
RequireUngrouped(command.get());
}
}
void CommandScheduler::RequireUngrouped(
std::initializer_list<const Command*> commands) {
for (auto&& command : commands) {
RequireUngrouped(command);
}
}
void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) { void CommandScheduler::InitSendable(nt::NTSendableBuilder& builder) {
builder.SetSmartDashboardType("Scheduler"); builder.SetSmartDashboardType("Scheduler");
builder.SetUpdateTable( builder.SetUpdateTable(

View File

@@ -10,15 +10,14 @@ ConditionalCommand::ConditionalCommand(std::unique_ptr<Command>&& onTrue,
std::unique_ptr<Command>&& onFalse, std::unique_ptr<Command>&& onFalse,
std::function<bool()> condition) std::function<bool()> condition)
: m_condition{std::move(condition)} { : m_condition{std::move(condition)} {
if (!CommandGroupBase::RequireUngrouped({onTrue.get(), onFalse.get()})) { CommandScheduler::GetInstance().RequireUngrouped(
return; {onTrue.get(), onFalse.get()});
}
m_onTrue = std::move(onTrue); m_onTrue = std::move(onTrue);
m_onFalse = std::move(onFalse); m_onFalse = std::move(onFalse);
m_onTrue->SetGrouped(true); m_onTrue->SetComposed(true);
m_onFalse->SetGrouped(true); m_onFalse->SetComposed(true);
m_runsWhenDisabled &= m_onTrue->RunsWhenDisabled(); m_runsWhenDisabled &= m_onTrue->RunsWhenDisabled();
m_runsWhenDisabled &= m_onFalse->RunsWhenDisabled(); m_runsWhenDisabled &= m_onFalse->RunsWhenDisabled();

View File

@@ -63,11 +63,7 @@ Command::InterruptionBehavior ParallelCommandGroup::GetInterruptionBehavior()
void ParallelCommandGroup::AddCommands( void ParallelCommandGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) { std::vector<std::unique_ptr<Command>>&& commands) {
for (auto&& command : commands) { CommandScheduler::GetInstance().RequireUngrouped(commands);
if (!RequireUngrouped(*command)) {
return;
}
}
if (isRunning) { if (isRunning) {
throw FRC_MakeError(frc::err::CommandIllegalUse, throw FRC_MakeError(frc::err::CommandIllegalUse,
@@ -77,7 +73,7 @@ void ParallelCommandGroup::AddCommands(
for (auto&& command : commands) { for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) { if (RequirementsDisjoint(this, command.get())) {
command->SetGrouped(true); command->SetComposed(true);
AddRequirements(command->GetRequirements()); AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled(); m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() == if (command->GetInterruptionBehavior() ==

View File

@@ -60,9 +60,7 @@ Command::InterruptionBehavior ParallelDeadlineGroup::GetInterruptionBehavior()
void ParallelDeadlineGroup::AddCommands( void ParallelDeadlineGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) { std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) { CommandScheduler::GetInstance().RequireUngrouped(commands);
return;
}
if (!m_finished) { if (!m_finished) {
throw FRC_MakeError(frc::err::CommandIllegalUse, throw FRC_MakeError(frc::err::CommandIllegalUse,
@@ -72,7 +70,7 @@ void ParallelDeadlineGroup::AddCommands(
for (auto&& command : commands) { for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) { if (RequirementsDisjoint(this, command.get())) {
command->SetGrouped(true); command->SetComposed(true);
AddRequirements(command->GetRequirements()); AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled(); m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() == if (command->GetInterruptionBehavior() ==
@@ -90,7 +88,7 @@ void ParallelDeadlineGroup::AddCommands(
void ParallelDeadlineGroup::SetDeadline(std::unique_ptr<Command>&& deadline) { void ParallelDeadlineGroup::SetDeadline(std::unique_ptr<Command>&& deadline) {
m_deadline = deadline.get(); m_deadline = deadline.get();
m_deadline->SetGrouped(true); m_deadline->SetComposed(true);
m_commands.emplace_back(std::move(deadline), false); m_commands.emplace_back(std::move(deadline), false);
AddRequirements(m_deadline->GetRequirements()); AddRequirements(m_deadline->GetRequirements());
m_runWhenDisabled &= m_deadline->RunsWhenDisabled(); m_runWhenDisabled &= m_deadline->RunsWhenDisabled();

View File

@@ -50,9 +50,7 @@ Command::InterruptionBehavior ParallelRaceGroup::GetInterruptionBehavior()
void ParallelRaceGroup::AddCommands( void ParallelRaceGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) { std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) { CommandScheduler::GetInstance().RequireUngrouped(commands);
return;
}
if (isRunning) { if (isRunning) {
throw FRC_MakeError(frc::err::CommandIllegalUse, throw FRC_MakeError(frc::err::CommandIllegalUse,
@@ -62,7 +60,7 @@ void ParallelRaceGroup::AddCommands(
for (auto&& command : commands) { for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) { if (RequirementsDisjoint(this, command.get())) {
command->SetGrouped(true); command->SetComposed(true);
AddRequirements(command->GetRequirements()); AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled(); m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() == if (command->GetInterruptionBehavior() ==

View File

@@ -7,11 +7,9 @@
using namespace frc2; using namespace frc2;
PerpetualCommand::PerpetualCommand(std::unique_ptr<Command>&& command) { PerpetualCommand::PerpetualCommand(std::unique_ptr<Command>&& command) {
if (!CommandGroupBase::RequireUngrouped(*command)) { CommandScheduler::GetInstance().RequireUngrouped(command.get());
return;
}
m_command = std::move(command); m_command = std::move(command);
m_command->SetGrouped(true); m_command->SetComposed(true);
AddRequirements(m_command->GetRequirements()); AddRequirements(m_command->GetRequirements());
} }

View File

@@ -7,11 +7,9 @@
using namespace frc2; using namespace frc2;
RepeatCommand::RepeatCommand(std::unique_ptr<Command>&& command) { RepeatCommand::RepeatCommand(std::unique_ptr<Command>&& command) {
if (!CommandGroupBase::RequireUngrouped(*command)) { CommandScheduler::GetInstance().RequireUngrouped(command.get());
return;
}
m_command = std::move(command); m_command = std::move(command);
m_command->SetGrouped(true); m_command->SetComposed(true);
AddRequirements(m_command->GetRequirements()); AddRequirements(m_command->GetRequirements());
} }

View File

@@ -60,9 +60,7 @@ Command::InterruptionBehavior SequentialCommandGroup::GetInterruptionBehavior()
void SequentialCommandGroup::AddCommands( void SequentialCommandGroup::AddCommands(
std::vector<std::unique_ptr<Command>>&& commands) { std::vector<std::unique_ptr<Command>>&& commands) {
if (!RequireUngrouped(commands)) { CommandScheduler::GetInstance().RequireUngrouped(commands);
return;
}
if (m_currentCommandIndex != invalid_index) { if (m_currentCommandIndex != invalid_index) {
throw FRC_MakeError(frc::err::CommandIllegalUse, throw FRC_MakeError(frc::err::CommandIllegalUse,
@@ -71,7 +69,7 @@ void SequentialCommandGroup::AddCommands(
} }
for (auto&& command : commands) { for (auto&& command : commands) {
command->SetGrouped(true); command->SetComposed(true);
AddRequirements(command->GetRequirements()); AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled(); m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() == if (command->GetInterruptionBehavior() ==

View File

@@ -9,11 +9,9 @@
using namespace frc2; using namespace frc2;
WrapperCommand::WrapperCommand(std::unique_ptr<Command>&& command) { WrapperCommand::WrapperCommand(std::unique_ptr<Command>&& command) {
if (!CommandGroupBase::RequireUngrouped(*command)) { CommandScheduler::GetInstance().RequireUngrouped(command.get());
return;
}
m_command = std::move(command); m_command = std::move(command);
m_command->SetGrouped(true); m_command->SetComposed(true);
} }
void WrapperCommand::Initialize() { void WrapperCommand::Initialize() {

View File

@@ -302,9 +302,9 @@ safe) semantics.
void Cancel(); void Cancel();
/** /**
* Whether or not the command is currently scheduled. Note that this does not * Whether or not the command is currently scheduled. Note that this does not
* detect whether the command is being run by a CommandGroup, only whether it * detect whether the command is in a composition, only whether it is directly
* is directly being run by the scheduler. * being run by the scheduler.
* *
* @return Whether the command is scheduled. * @return Whether the command is scheduled.
*/ */
@@ -324,13 +324,32 @@ safe) semantics.
* Whether the command is currently grouped in a command group. Used as extra * Whether the command is currently grouped in a command group. Used as extra
* insurance to prevent accidental independent use of grouped commands. * 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; bool IsGrouped() const;
/** /**
* Sets whether the command is currently grouped in a command group. Can be * 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 * used to "reclaim" a command if a group is no longer going to use it. NOT
* ADVISED! * ADVISED!
*
* @deprecated Moved to SetComposed()
*/ */
WPI_DEPRECATED("Moved to SetComposed()")
void SetGrouped(bool grouped); void SetGrouped(bool grouped);
/** /**
@@ -379,7 +398,7 @@ safe) semantics.
*/ */
virtual std::unique_ptr<Command> TransferOwnership() && = 0; virtual std::unique_ptr<Command> TransferOwnership() && = 0;
bool m_isGrouped = false; bool m_isComposed = false;
}; };
/** /**

View File

@@ -4,61 +4,24 @@
#pragma once #pragma once
#include <initializer_list>
#include <memory> #include <memory>
#include <span>
#include <vector> #include <vector>
#include <wpi/deprecated.h>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
namespace frc2 { namespace frc2 {
/** /**
* A base for CommandGroups. Statically tracks commands that have been * A base for CommandGroups.
* allocated to groups to ensure those commands are not also used independently,
* which can result in inconsistent command state and unpredictable execution.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
* @deprecated This class is an empty abstraction. Inherit directly from
* CommandBase.
*/ */
class CommandGroupBase : public CommandBase { class CommandGroupBase : public CommandBase {
public: public:
/**
* Requires that the specified command not have been already allocated to a
* CommandGroup. Reports an error if the command is already grouped.
*
* @param command The command to check
* @return True if all the command is ungrouped.
*/
static bool RequireUngrouped(const Command& command);
/**
* Requires that the specified command not have been already allocated to a
* CommandGroup. Reports an error if the command is already grouped.
*
* @param command The command to check
* @return True if all the command is ungrouped.
*/
static bool RequireUngrouped(const Command* command);
/**
* Requires that the specified commands not have been already allocated to a
* CommandGroup. Reports an error if any of the commands are already grouped.
*
* @param commands The commands to check
* @return True if all the commands are ungrouped.
*/
static bool RequireUngrouped(
std::span<const std::unique_ptr<Command>> commands);
/**
* Requires that the specified commands not have been already allocated to a
* CommandGroup. Reports an error if any of the commands are already grouped.
*
* @param commands The commands to check
* @return True if all the commands are ungrouped.
*/
static bool RequireUngrouped(std::initializer_list<const Command*> commands);
/** /**
* Adds the given commands to the command group. * Adds the given commands to the command group.
* *

View File

@@ -252,8 +252,8 @@ class CommandPtr final {
/** /**
* Whether or not the command is currently scheduled. Note that this does not * Whether or not the command is currently scheduled. Note that this does not
* detect whether the command is being run by a CommandGroup, only whether it * detect whether the command is in a composition, only whether it is directly
* is directly being run by the scheduler. * being run by the scheduler.
* *
* @return Whether the command is scheduled. * @return Whether the command is scheduled.
*/ */

View File

@@ -360,6 +360,34 @@ class CommandScheduler final : public nt::NTSendable,
*/ */
void OnCommandFinish(Action action); void OnCommandFinish(Action action);
/**
* Requires that the specified command hasn't been already added to a
* composition.
*
* @param command The command to check
* @throws if the given commands have already been composed.
*/
void RequireUngrouped(const Command* command);
/**
* Requires that the specified commands not have been already added to a
* composition.
*
* @param commands The commands to check
* @throws if the given commands have already been composed.
*/
void RequireUngrouped(std::span<const std::unique_ptr<Command>> commands);
/**
* Requires that the specified commands not have been already added to a
* composition.
*
* @param commands The commands to check
* @throws IllegalArgumentException if the given commands have already been
* composed.
*/
void RequireUngrouped(std::initializer_list<const Command*> commands);
void InitSendable(nt::NTSendableBuilder& builder) override; void InitSendable(nt::NTSendableBuilder& builder) override;
private: private:

View File

@@ -9,25 +9,17 @@
#include <utility> #include <utility>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandHelper.h" #include "frc2/command/CommandHelper.h"
namespace frc2 { namespace frc2 {
/** /**
* Runs one of two commands, depending on the value of the given condition when * A command composition that runs one of two commands, depending on the value
* this command is initialized. Does not actually schedule the selected command * of the given condition when this command is initialized.
* - rather, the command is run through this command; this ensures that the
* command will behave as expected if used as part of a CommandGroup. Requires
* the requirements of both commands, again to ensure proper functioning when
* used in a CommandGroup. If this is undesired, consider using
* ScheduleCommand.
* *
* <p>As this command contains multiple component commands within it, it is * <p>The rules for command compositions apply: command instances that are
* technically a command group; the command instances that are passed to it * passed to it are owned by the composition and cannot be added to any other
* cannot be added to any other groups, or scheduled individually. * composition or scheduled individually, and the composition requires all
* * subsystems its components require.
* <p>As a rule, CommandGroups require the union of the requirements of their
* component commands.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
* *

View File

@@ -18,11 +18,13 @@
namespace frc2 { namespace frc2 {
/** /**
* A CommandGroup that runs a set of commands in parallel, ending when the last * A command composition that runs a set of commands in parallel, ending when
* command ends. * the last command ends.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their * <p>The rules for command compositions apply: command instances that are
* component commands. * passed to it are owned by the composition and cannot be added to any other
* composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */
@@ -30,23 +32,23 @@ class ParallelCommandGroup
: public CommandHelper<CommandGroupBase, ParallelCommandGroup> { : public CommandHelper<CommandGroupBase, ParallelCommandGroup> {
public: public:
/** /**
* Creates a new ParallelCommandGroup. The given commands will be executed * Creates a new ParallelCommandGroup. The given commands will be executed
* simultaneously. The command group will finish when the last command * simultaneously. The command group will finish when the last command
* finishes. If the CommandGroup is interrupted, only the commands that are * finishes. If the composition is interrupted, only the commands that are
* still running will be interrupted. * still running will be interrupted.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
explicit ParallelCommandGroup( explicit ParallelCommandGroup(
std::vector<std::unique_ptr<Command>>&& commands); std::vector<std::unique_ptr<Command>>&& commands);
/** /**
* Creates a new ParallelCommandGroup. The given commands will be executed * Creates a new ParallelCommandGroup. The given commands will be executed
* simultaneously. The command group will finish when the last command * simultaneously. The command group will finish when the last command
* finishes. If the CommandGroup is interrupted, only the commands that are * finishes. If the composition is interrupted, only the commands that are
* still running will be interrupted. * still running will be interrupted.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
template <class... Types, template <class... Types,
typename = std::enable_if_t<std::conjunction_v< typename = std::enable_if_t<std::conjunction_v<

View File

@@ -18,12 +18,14 @@
namespace frc2 { namespace frc2 {
/** /**
* A CommandGroup that runs a set of commands in parallel, ending only when a * A command composition that runs a set of commands in parallel, ending only
* specific command (the "deadline") ends, interrupting all other commands that * when a specific command (the "deadline") ends, interrupting all other
* are still running at that point. * commands that are still running at that point.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their * <p>The rules for command compositions apply: command instances that are
* component commands. * passed to it are owned by the composition and cannot be added to any other
* composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */
@@ -31,25 +33,25 @@ class ParallelDeadlineGroup
: public CommandHelper<CommandGroupBase, ParallelDeadlineGroup> { : public CommandHelper<CommandGroupBase, ParallelDeadlineGroup> {
public: public:
/** /**
* Creates a new ParallelDeadlineGroup. The given commands (including the * Creates a new ParallelDeadlineGroup. The given commands (including the
* deadline) will be executed simultaneously. The CommandGroup will finish * deadline) will be executed simultaneously. The composition will finish when
* when the deadline finishes, interrupting all other still-running commands. * the deadline finishes, interrupting all other still-running commands. If
* If the CommandGroup is interrupted, only the commands still running will be * the composition is interrupted, only the commands still running will be
* interrupted. * interrupted.
* *
* @param deadline the command that determines when the group ends * @param deadline the command that determines when the composition ends
* @param commands the commands to be executed * @param commands the commands to be executed
*/ */
ParallelDeadlineGroup(std::unique_ptr<Command>&& deadline, ParallelDeadlineGroup(std::unique_ptr<Command>&& deadline,
std::vector<std::unique_ptr<Command>>&& commands); std::vector<std::unique_ptr<Command>>&& commands);
/** /**
* Creates a new ParallelDeadlineGroup. The given commands (including the * Creates a new ParallelDeadlineGroup. The given commands (including the
* deadline) will be executed simultaneously. The CommandGroup will finish * deadline) will be executed simultaneously. The composition will finish when
* when the deadline finishes, interrupting all other still-running commands. * the deadline finishes, interrupting all other still-running commands. If
* If the CommandGroup is interrupted, only the commands still running will be * the composition is interrupted, only the commands still running will be
* interrupted. * interrupted.
* *
* @param deadline the command that determines when the group ends * @param deadline the command that determines when the composition ends
* @param commands the commands to be executed * @param commands the commands to be executed
*/ */
template <class T, class... Types, template <class T, class... Types,

View File

@@ -18,11 +18,13 @@
namespace frc2 { namespace frc2 {
/** /**
* A CommandGroup that runs a set of commands in parallel, ending when any one * A composition that runs a set of commands in parallel, ending when any one of
* of the commands ends and interrupting all the others. * the commands ends and interrupting all the others.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their * <p>The rules for command compositions apply: command instances that are
* component commands. * passed to it are owned by the composition and cannot be added to any other
* composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */
@@ -30,11 +32,11 @@ class ParallelRaceGroup
: public CommandHelper<CommandGroupBase, ParallelRaceGroup> { : public CommandHelper<CommandGroupBase, ParallelRaceGroup> {
public: public:
/** /**
* Creates a new ParallelCommandRace. The given commands will be executed * Creates a new ParallelCommandRace. The given commands will be executed
* simultaneously, and will "race to the finish" - the first command to finish * simultaneously, and will "race to the finish" - the first command to finish
* ends the entire command, with all other commands being interrupted. * ends the entire command, with all other commands being interrupted.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
explicit ParallelRaceGroup(std::vector<std::unique_ptr<Command>>&& commands); explicit ParallelRaceGroup(std::vector<std::unique_ptr<Command>>&& commands);

View File

@@ -13,13 +13,12 @@
#include <utility> #include <utility>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandHelper.h" #include "frc2/command/CommandHelper.h"
namespace frc2 { namespace frc2 {
/** /**
* A command that runs another command in perpetuity, ignoring that command's * A command that runs another command in perpetuity, ignoring that command's
* end conditions. While this class does not extend {@link CommandGroupBase}, * end conditions. While this class does not extend frc2::CommandGroupBase,
* it is still considered a CommandGroup, as it allows one to compose another * it is still considered a CommandGroup, as it allows one to compose another
* command within it; the command instances that are passed to it cannot be * command within it; the command instances that are passed to it cannot be
* added to any other groups, or scheduled individually. * added to any other groups, or scheduled individually.

View File

@@ -13,19 +13,18 @@
#include <utility> #include <utility>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandHelper.h" #include "frc2/command/CommandHelper.h"
namespace frc2 { namespace frc2 {
/** /**
* A command that runs another command repeatedly, restarting it when it ends, * A command that runs another command repeatedly, restarting it when it ends,
* until this command is interrupted. While this class does not extend {@link * until this command is interrupted. Command instances that are passed to it
* CommandGroupBase}, it is still considered a CommandGroup, as it allows one to * cannot be added to any other groups, or scheduled individually.
* compose another command within it; the command instances that are passed to
* it cannot be added to any other groups, or scheduled individually.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their * <p>The rules for command compositions apply: command instances that are
* component commands. * passed to it are owned by the composition and cannot be added to any other
* composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* <p>This class is provided by the NewCommands VendorDep * <p>This class is provided by the NewCommands VendorDep
*/ */

View File

@@ -14,10 +14,10 @@
namespace frc2 { namespace frc2 {
/** /**
* Schedules the given commands when this command is initialized. Useful for * Schedules the given commands when this command is initialized. Useful for
* forking off from CommandGroups. Note that if run from a CommandGroup, the * forking off from CommandGroups. Note that if run from a composition, the
* group will not know about the status of the scheduled commands, and will * composition will not know about the status of the scheduled commands, and
* treat this command as finishing instantly. * will treat this command as finishing instantly.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */

View File

@@ -16,25 +16,18 @@
#include <vector> #include <vector>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/PrintCommand.h" #include "frc2/command/PrintCommand.h"
namespace frc2 { namespace frc2 {
/** /**
* Runs one of a selection of commands, either using a selector and a key to * A command composition that runs one of a selection of commands, either using
* command mapping, or a supplier that returns the command directly at runtime. * a selector and a key to command mapping, or a supplier that returns the
* Does not actually schedule the selected command - rather, the command is run * command directly at runtime.
* through this command; this ensures that the command will behave as expected
* if used as part of a CommandGroup. Requires the requirements of all included
* commands, again to ensure proper functioning when used in a CommandGroup. If
* this is undesired, consider using ScheduleCommand.
* *
* <p>As this command contains multiple component commands within it, it is * <p>The rules for command compositions apply: command instances that are
* technically a command group; the command instances that are passed to it * passed to it are owned by the composition and cannot be added to any other
* cannot be added to any other groups, or scheduled individually. * composition or scheduled individually, and the composition requires all
* * subsystems its components require.
* <p>As a rule, CommandGroups require the union of the requirements of their
* component commands.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */
@@ -61,9 +54,7 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
...); ...);
for (auto&& command : foo) { for (auto&& command : foo) {
if (!CommandGroupBase::RequireUngrouped(*command.second)) { CommandScheduler::GetInstance().RequireUngrouped(command.second.get());
return;
}
} }
for (auto&& command : foo) { for (auto&& command : foo) {
@@ -82,9 +73,7 @@ class SelectCommand : public CommandHelper<CommandBase, SelectCommand<Key>> {
std::vector<std::pair<Key, std::unique_ptr<Command>>>&& commands) std::vector<std::pair<Key, std::unique_ptr<Command>>>&& commands)
: m_selector{std::move(selector)} { : m_selector{std::move(selector)} {
for (auto&& command : commands) { for (auto&& command : commands) {
if (!CommandGroupBase::RequireUngrouped(*command.second)) { CommandScheduler::GetInstance().RequireUngrouped(command.second.get());
return;
}
} }
for (auto&& command : commands) { for (auto&& command : commands) {

View File

@@ -24,10 +24,12 @@ namespace frc2 {
const size_t invalid_index = std::numeric_limits<size_t>::max(); const size_t invalid_index = std::numeric_limits<size_t>::max();
/** /**
* A CommandGroups that runs a list of commands in sequence. * A command composition that runs a list of commands in sequence.
* *
* <p>As a rule, CommandGroups require the union of the requirements of their * <p>The rules for command compositions apply: command instances that are
* component commands. * passed to it are owned by the composition and cannot be added to any other
* composition or scheduled individually, and the composition requires all
* subsystems its components require.
* *
* This class is provided by the NewCommands VendorDep * This class is provided by the NewCommands VendorDep
*/ */
@@ -35,21 +37,21 @@ class SequentialCommandGroup
: public CommandHelper<CommandGroupBase, SequentialCommandGroup> { : public CommandHelper<CommandGroupBase, SequentialCommandGroup> {
public: public:
/** /**
* Creates a new SequentialCommandGroup. The given commands will be run * Creates a new SequentialCommandGroup. The given commands will be run
* sequentially, with the CommandGroup finishing when the last command * sequentially, with the composition finishing when the last command
* finishes. * finishes.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
explicit SequentialCommandGroup( explicit SequentialCommandGroup(
std::vector<std::unique_ptr<Command>>&& commands); std::vector<std::unique_ptr<Command>>&& commands);
/** /**
* Creates a new SequentialCommandGroup. The given commands will be run * Creates a new SequentialCommandGroup. The given commands will be run
* sequentially, with the CommandGroup finishing when the last command * sequentially, with the composition finishing when the last command
* finishes. * finishes.
* *
* @param commands the commands to include in this group. * @param commands the commands to include in this composition.
*/ */
template <class... Types, template <class... Types,
typename = std::enable_if_t<std::conjunction_v< typename = std::enable_if_t<std::conjunction_v<

View File

@@ -13,7 +13,6 @@
#include <utility> #include <utility>
#include "frc2/command/CommandBase.h" #include "frc2/command/CommandBase.h"
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandHelper.h" #include "frc2/command/CommandHelper.h"
namespace frc2 { namespace frc2 {

View File

@@ -24,16 +24,15 @@ class CommandGroupErrorTest extends CommandTestBase {
@Test @Test
void commandInGroupExternallyScheduledTest() { void commandInGroupExternallyScheduledTest() {
try (CommandScheduler scheduler = new CommandScheduler()) { MockCommandHolder command1Holder = new MockCommandHolder(true);
MockCommandHolder command1Holder = new MockCommandHolder(true); Command command1 = command1Holder.getMock();
Command command1 = command1Holder.getMock(); MockCommandHolder command2Holder = new MockCommandHolder(true);
MockCommandHolder command2Holder = new MockCommandHolder(true); Command command2 = command2Holder.getMock();
Command command2 = command2Holder.getMock();
new ParallelCommandGroup(command1, command2); new ParallelCommandGroup(command1, command2);
assertThrows(IllegalArgumentException.class, () -> scheduler.schedule(command1)); assertThrows(
} IllegalArgumentException.class, () -> CommandScheduler.getInstance().schedule(command1));
} }
@Test @Test
@@ -42,7 +41,7 @@ class CommandGroupErrorTest extends CommandTestBase {
assertDoesNotThrow(() -> command.withTimeout(10).until(() -> false)); assertDoesNotThrow(() -> command.withTimeout(10).until(() -> false));
assertThrows(IllegalArgumentException.class, () -> command.withTimeout(10)); assertThrows(IllegalArgumentException.class, () -> command.withTimeout(10));
CommandGroupBase.clearGroupedCommand(command); CommandScheduler.getInstance().removeComposedCommand(command);
assertDoesNotThrow(() -> command.withTimeout(10)); assertDoesNotThrow(() -> command.withTimeout(10));
} }
} }

View File

@@ -22,7 +22,7 @@ public class CommandTestBase {
CommandScheduler.getInstance().cancelAll(); CommandScheduler.getInstance().cancelAll();
CommandScheduler.getInstance().enable(); CommandScheduler.getInstance().enable();
CommandScheduler.getInstance().getActiveButtonLoop().clear(); CommandScheduler.getInstance().getActiveButtonLoop().clear();
CommandGroupBase.clearGroupedCommands(); CommandScheduler.getInstance().clearComposedCommands();
setDSEnabled(true); setDSEnabled(true);
} }

View File

@@ -9,7 +9,6 @@
#include <frc/simulation/DriverStationSim.h> #include <frc/simulation/DriverStationSim.h>
#include "frc2/command/CommandGroupBase.h"
#include "frc2/command/CommandHelper.h" #include "frc2/command/CommandHelper.h"
#include "frc2/command/CommandScheduler.h" #include "frc2/command/CommandScheduler.h"
#include "frc2/command/SetUtilities.h" #include "frc2/command/SetUtilities.h"

View File

@@ -8,6 +8,7 @@ import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Drivetrain; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Drivetrain;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
/** The main autonomous command to pickup and deliver the soda to the box. */ /** The main autonomous command to pickup and deliver the soda to the box. */
@@ -22,6 +23,6 @@ public class Autonomous extends SequentialCommandGroup {
new Place(claw, wrist, elevator), new Place(claw, wrist, elevator),
new SetDistanceToBox(0.60, drive), new SetDistanceToBox(0.60, drive),
// new DriveStraight(-2), // Use Encoders if ultrasonic is broken // new DriveStraight(-2), // Use Encoders if ultrasonic is broken
parallel(new SetWristSetpoint(-45, wrist), new CloseClaw(claw))); Commands.parallel(new SetWristSetpoint(-45, wrist), new CloseClaw(claw)));
} }
} }

View File

@@ -7,6 +7,7 @@ package edu.wpi.first.wpilibj.examples.gearsbot.commands;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
/** /**
@@ -23,6 +24,7 @@ public class Pickup extends SequentialCommandGroup {
public Pickup(Claw claw, Wrist wrist, Elevator elevator) { public Pickup(Claw claw, Wrist wrist, Elevator elevator) {
addCommands( addCommands(
new CloseClaw(claw), new CloseClaw(claw),
parallel(new SetWristSetpoint(-45, wrist), new SetElevatorSetpoint(0.25, elevator))); Commands.parallel(
new SetWristSetpoint(-45, wrist), new SetElevatorSetpoint(0.25, elevator)));
} }
} }

View File

@@ -7,6 +7,7 @@ package edu.wpi.first.wpilibj.examples.gearsbot.commands;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Claw;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Elevator;
import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist; import edu.wpi.first.wpilibj.examples.gearsbot.subsystems.Wrist;
import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
/** Make sure the robot is in a state to pickup soda cans. */ /** Make sure the robot is in a state to pickup soda cans. */
@@ -21,6 +22,6 @@ public class PrepareToPickup extends SequentialCommandGroup {
public PrepareToPickup(Claw claw, Wrist wrist, Elevator elevator) { public PrepareToPickup(Claw claw, Wrist wrist, Elevator elevator) {
addCommands( addCommands(
new OpenClaw(claw), new OpenClaw(claw),
parallel(new SetWristSetpoint(0, wrist), new SetElevatorSetpoint(0, elevator))); Commands.parallel(new SetWristSetpoint(0, wrist), new SetElevatorSetpoint(0, elevator)));
} }
} }

View File

@@ -4,7 +4,7 @@
package edu.wpi.first.wpilibj.examples.rapidreactcommandbot; package edu.wpi.first.wpilibj.examples.rapidreactcommandbot;
import static edu.wpi.first.wpilibj2.command.CommandGroupBase.parallel; import static edu.wpi.first.wpilibj2.command.Commands.parallel;
import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.AutoConstants; import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.AutoConstants;
import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.OIConstants; import edu.wpi.first.wpilibj.examples.rapidreactcommandbot.Constants.OIConstants;