001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.command;
009
010import java.util.Enumeration;
011
012import edu.wpi.first.wpilibj.Sendable;
013import edu.wpi.first.wpilibj.SendableBase;
014import edu.wpi.first.wpilibj.livewindow.LiveWindow;
015import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
016
017/**
018 * This class defines a major component of the robot.
019 *
020 * <p> A good example of a subsystem is the driveline, or a claw if the robot has one. </p>
021 *
022 * <p> All motors should be a part of a subsystem. For instance, all the wheel motors should be a
023 * part of some kind of "Driveline" subsystem. </p>
024 *
025 * <p> Subsystems are used within the command system as requirements for {@link Command}. Only one
026 * command which requires a subsystem can run at a time. Also, subsystems can have default commands
027 * which are started if there is no command running which requires this subsystem. </p>
028 *
029 * @see Command
030 */
031public abstract class Subsystem extends SendableBase implements Sendable {
032  /**
033   * Whether or not getDefaultCommand() was called.
034   */
035  private boolean m_initializedDefaultCommand = false;
036  /**
037   * The current command.
038   */
039  private Command m_currentCommand;
040  private boolean m_currentCommandChanged;
041
042  /**
043   * The default command.
044   */
045  private Command m_defaultCommand;
046
047  /**
048   * Creates a subsystem with the given name.
049   *
050   * @param name the name of the subsystem
051   */
052  public Subsystem(String name) {
053    setName(name, name);
054    Scheduler.getInstance().registerSubsystem(this);
055  }
056
057  /**
058   * Creates a subsystem. This will set the name to the name of the class.
059   */
060  public Subsystem() {
061    String name = getClass().getName();
062    name = name.substring(name.lastIndexOf('.') + 1);
063    setName(name, name);
064    Scheduler.getInstance().registerSubsystem(this);
065    m_currentCommandChanged = true;
066  }
067
068  /**
069   * Initialize the default command for a subsystem By default subsystems have no default command,
070   * but if they do, the default command is set with this method. It is called on all Subsystems by
071   * CommandBase in the users program after all the Subsystems are created.
072   */
073  protected abstract void initDefaultCommand();
074
075  /**
076   * When the run method of the scheduler is called this method will be called.
077   */
078  public void periodic() {
079    // Override me!
080  }
081
082  /**
083   * Sets the default command. If this is not called or is called with null, then there will be no
084   * default command for the subsystem.
085   *
086   * <p> <b>WARNING:</b> This should <b>NOT</b> be called in a constructor if the subsystem is a
087   * singleton. </p>
088   *
089   * @param command the default command (or null if there should be none)
090   * @throws IllegalUseOfCommandException if the command does not require the subsystem
091   */
092  public void setDefaultCommand(Command command) {
093    if (command == null) {
094      m_defaultCommand = null;
095    } else {
096      boolean found = false;
097      Enumeration requirements = command.getRequirements();
098      while (requirements.hasMoreElements()) {
099        if (requirements.nextElement().equals(this)) {
100          found = true;
101          // } else {
102          // throw new
103          // IllegalUseOfCommandException("A default command cannot require multiple subsystems");
104        }
105      }
106      if (!found) {
107        throw new IllegalUseOfCommandException("A default command must require the subsystem");
108      }
109      m_defaultCommand = command;
110    }
111  }
112
113  /**
114   * Returns the default command (or null if there is none).
115   *
116   * @return the default command
117   */
118  public Command getDefaultCommand() {
119    if (!m_initializedDefaultCommand) {
120      m_initializedDefaultCommand = true;
121      initDefaultCommand();
122    }
123    return m_defaultCommand;
124  }
125
126  /**
127   * Returns the default command name, or empty string is there is none.
128   *
129   * @return the default command name
130   */
131  public String getDefaultCommandName() {
132    Command defaultCommand = getDefaultCommand();
133    if (defaultCommand != null) {
134      return defaultCommand.getName();
135    } else {
136      return "";
137    }
138  }
139
140  /**
141   * Sets the current command.
142   *
143   * @param command the new current command
144   */
145  void setCurrentCommand(Command command) {
146    m_currentCommand = command;
147    m_currentCommandChanged = true;
148  }
149
150  /**
151   * Call this to alert Subsystem that the current command is actually the command. Sometimes, the
152   * {@link Subsystem} is told that it has no command while the {@link Scheduler} is going through
153   * the loop, only to be soon after given a new one. This will avoid that situation.
154   */
155  void confirmCommand() {
156    if (m_currentCommandChanged) {
157      m_currentCommandChanged = false;
158    }
159  }
160
161  /**
162   * Returns the command which currently claims this subsystem.
163   *
164   * @return the command which currently claims this subsystem
165   */
166  public Command getCurrentCommand() {
167    return m_currentCommand;
168  }
169
170  /**
171   * Returns the current command name, or empty string if no current command.
172   *
173   * @return the current command name
174   */
175  public String getCurrentCommandName() {
176    Command currentCommand = getCurrentCommand();
177    if (currentCommand != null) {
178      return currentCommand.getName();
179    } else {
180      return "";
181    }
182  }
183
184  /**
185   * Associate a {@link Sendable} with this Subsystem.
186   * Also update the child's name.
187   *
188   * @param name name to give child
189   * @param child sendable
190   */
191  public void addChild(String name, Sendable child) {
192    child.setName(getSubsystem(), name);
193    LiveWindow.add(child);
194  }
195
196  /**
197   * Associate a {@link Sendable} with this Subsystem.
198   *
199   * @param child sendable
200   */
201  public void addChild(Sendable child) {
202    child.setSubsystem(getSubsystem());
203    LiveWindow.add(child);
204  }
205
206  @Override
207  public String toString() {
208    return getSubsystem();
209  }
210
211  @Override
212  public void initSendable(SendableBuilder builder) {
213    builder.setSmartDashboardType("Subsystem");
214
215    builder.addBooleanProperty(".hasDefault", () -> m_defaultCommand != null, null);
216    builder.addStringProperty(".default", this::getDefaultCommandName, null);
217    builder.addBooleanProperty(".hasCommand", () -> m_currentCommand != null, null);
218    builder.addStringProperty(".command", this::getCurrentCommandName, null);
219  }
220}