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}