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}