Subsystem & Command Conventions - Team1100/CodeExamples GitHub Wiki
This is what makes people not immediately think you're an idiot when they see your code.
I'm serious. If I'm still alive by the time you read this, hit me up for some great stories.
'Great' is relative. This is programming humor.
If you're reading this page, I assume you've already read the page about the command-based robot. If not, well, I can't really stop you. I'm just words written by a programmer who may be long gone by the time you read this. If you're my great-great-great grandson on a quest to avenge me, this is the 796th clue. They are behind you, but they do not know about the crossbow. Use this information to your advantage.
##Subsystems Let's start off with how subsystems work. On Team 2^2 * 5^2 * 11, we reference subsystems with a static getInstance() method. There is a very specific way to write this. Let's pretend I am writing a subsystem for the photon torpedos.
public class PhotonTorpedo extends Subsystem(){
private static PhotonTorpedo photonTorpedo;
public static getInstance() {
if(photonTorpdo == null) photonTorpedo = new PhotonTorpedo();
return photonTorpedo;
}
}
These subsystems have objects that represent important objects on the actual robot, like motor controllers (Talons, Victors, CANTalon), pneumatic components(DoubleSolenoids) or sensors (AnalogGyro, AnalogEncoder, DigitalInput). We declare them as private, and initialize them in the constructor.
public class GravityMultiplier extends Subsystem{
private CANTalon blackHoleWinchMotor;
private CANTalon eventHorizonRotator;
private DoubleSolenoid unSpaghettifier;
private DigitalInput deathSensor;
public GravityMultiplier(){
blackHoleWinchMotor = new CANTalon(RobotMap.G_BLACK_HOLE_WINCH_MOTOR);
eventHorizonRotator = new CANTalon(RobotMap.G_EVENT_HORIZON_ROTATOR);
unSpaghettifier = new DoubleSolenoid(RobotMap.G_PCM,RobotMap.G_SPAGHETTI_A,RobotMap.G_SPAGHETTI_B);
deathSensor = new DigitalInput(RobotMap.G_DEATH_SENSOR);
}
}
Take note on the constructor arguments of the various objects. This will be elaborated more when we discuss RobotMap, but essentially the argument is the port of the object on the robot, and we store all these values as static integers in RobotMap.java.
Any methods that interact with these hardware objects get called in this class as public methods. These will be called later in commands.
public void setWarpspeedMotor(double speed){
warpspeedMotor.set(speed);
}
public void fireGlitterTrail(){
glitterPiston.set(DoubleSolenoid.Value.kForward);
}
public double getEngineHeat(){
engineThermometer.get();
}
Finally, if you are creating a subsystem that is constantly waiting for input like an arm, drive train, or cotton candy machine, be sure to set a default command. This usually is accepting some analog value like a joystick or the volume of your enemy's screams. This is accomplished in the following manner:
@Override
protected void initDefaultCommand(){
setDefaultCommand(new killMortalsFromToeSticks());
}
##Commands Commands. Because we are straight-up savages, we overload our constructors. One normal, and one with another parameter, a long called timeout. In the constructor, add the statement setTimeout(timeout), then set isFinished() to return isTimedout. This will create a command that runs for (timeout) seconds.
public class JuiceMangoCommand extends Command {
private double mangoCount;
private double mangoSize;
private double mangoId;
public JuiceMangoCommand(double mangoCount, double mangoSize, double mangoId, double time) {
requires(Juicer.getInstance());
setTimeout(time);
this.mangoCount = mangoCount;
this.mangoSize = mangoSize;
this.mangoId = mangoId;
}
public void execute() {
Mango.getInstance().juiceMango(mangoCount,mangoSize,mangoId);
}
@Override
protected boolean isFinished() {
return isTimedOut();
}
}
Take note of the line that "requires" the jucier subsystem. This line is needed to make sure no other command is using the juicer instance. If so, they get ended.
But what if you want a command to run without a timeout, like in the case of a Solenoid firing? Make a private boolean to store whether or not the task has been completed, and have isFinished() return it.
private boolean done;
public FireBananaCommand(){
requires(Cannon.getInstance());
done = false;
}
public void execute(){
Cannon.getInstance().fireBanana();
done = true;
}
public boolean isFinished(){
return done;
}
For default command, just leave isFinished() as returning false.
If you want to do a specific action when the command is initialized(which occurs when the robot initializes the OI class), or when the command is interrupted or ended, put them in those respective methods.
public void initialize(){
System.err.println("Initializing grind broccoli command");
}
public void interrupted(){
end();
}
public void end(){
Broccoli.getInstance().ceaseRolling();
}