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}