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