001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj2.command;
006
007import edu.wpi.first.util.sendable.SendableBuilder;
008import java.util.ArrayList;
009import java.util.List;
010
011/**
012 * A command composition that runs a list of commands in sequence.
013 *
014 * <p>The rules for command compositions apply: command instances that are passed to it cannot be
015 * added to any other composition or scheduled individually, and the composition requires all
016 * subsystems its components require.
017 *
018 * <p>This class is provided by the NewCommands VendorDep
019 */
020public class SequentialCommandGroup extends Command {
021  private final List<Command> m_commands = new ArrayList<>();
022  private int m_currentCommandIndex = -1;
023  private boolean m_runWhenDisabled = true;
024  private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
025
026  /**
027   * Creates a new SequentialCommandGroup. The given commands will be run sequentially, with the
028   * composition finishing when the last command finishes.
029   *
030   * @param commands the commands to include in this composition.
031   */
032  @SuppressWarnings("this-escape")
033  public SequentialCommandGroup(Command... commands) {
034    addCommands(commands);
035  }
036
037  /**
038   * Adds the given commands to the group.
039   *
040   * @param commands Commands to add, in order of execution.
041   */
042  @SuppressWarnings("PMD.UseArraysAsList")
043  public final void addCommands(Command... commands) {
044    if (m_currentCommandIndex != -1) {
045      throw new IllegalStateException(
046          "Commands cannot be added to a composition while it's running");
047    }
048
049    CommandScheduler.getInstance().registerComposedCommands(commands);
050
051    for (Command command : commands) {
052      m_commands.add(command);
053      addRequirements(command.getRequirements());
054      m_runWhenDisabled &= command.runsWhenDisabled();
055      if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
056        m_interruptBehavior = InterruptionBehavior.kCancelSelf;
057      }
058    }
059  }
060
061  @Override
062  public final void initialize() {
063    m_currentCommandIndex = 0;
064
065    if (!m_commands.isEmpty()) {
066      m_commands.get(0).initialize();
067    }
068  }
069
070  @Override
071  public final void execute() {
072    if (m_commands.isEmpty()) {
073      return;
074    }
075
076    Command currentCommand = m_commands.get(m_currentCommandIndex);
077
078    currentCommand.execute();
079    if (currentCommand.isFinished()) {
080      currentCommand.end(false);
081      m_currentCommandIndex++;
082      if (m_currentCommandIndex < m_commands.size()) {
083        m_commands.get(m_currentCommandIndex).initialize();
084      }
085    }
086  }
087
088  @Override
089  public final void end(boolean interrupted) {
090    if (interrupted
091        && !m_commands.isEmpty()
092        && m_currentCommandIndex > -1
093        && m_currentCommandIndex < m_commands.size()) {
094      m_commands.get(m_currentCommandIndex).end(true);
095    }
096    m_currentCommandIndex = -1;
097  }
098
099  @Override
100  public final boolean isFinished() {
101    return m_currentCommandIndex == m_commands.size();
102  }
103
104  @Override
105  public boolean runsWhenDisabled() {
106    return m_runWhenDisabled;
107  }
108
109  @Override
110  public InterruptionBehavior getInterruptionBehavior() {
111    return m_interruptBehavior;
112  }
113
114  @Override
115  public void initSendable(SendableBuilder builder) {
116    super.initSendable(builder);
117
118    builder.addIntegerProperty("index", () -> m_currentCommandIndex, null);
119  }
120}