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