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}