Skip to content

Using a QuickCommand to simplify commands

blucoat edited this page Jan 25, 2016 · 4 revisions

Introduction

In the previous tutorial, we saw how to make a UserTankDrive command to drive the robot. Although the command was effectively one line of "real code", we had to make a separate class and implement five methods, plus a constructor. Not only is this time-consuming, it makes the code seem more complicated than it actually is, and means it's harder for us to quickly try out new ideas. Enter QuickCommand: a class that makes one-liners really one line, so you can focus on the interesting stuff.

Getting the QuickCommand class

We need to work on packaging the classes into a jar or something. For now just copy the class from this repo.

Changing UserTankDrive into a QuickCommand

Let's look at our tank drive command from last time:

public class UserTankDrive extends Command {

    public UserTankDrive() {
        requires(Robot.drivetrain);
    }
    
    protected void initialize() {
        // do nothing
    }

    protected void execute() {
        Robot.drivetrain.drive.tankDrive(Robot.oi.leftJoystick, Robot.oi.rightJoystick);
    }

    protected boolean isFinished() {
        return false;
    }

    protected void end() {
	    // do nothing
    }

    protected void interrupted() {
	    // do nothing
    }
}

Really, this command is saying three things:

  • We require Robot.drivetrain
  • We want to run Robot.drivetrain.drive.tankDrive(Robot.oi.leftJoystick, Robot.oi.rightJoystick);
  • We want to keep running it indefinitely

With a quickcommand, this can be expressed as:

QuickCommand.continuous(Robot.drivetrain, () -> Robot.drivetrain.drive.tankDrive(Robot.oi.leftJoystick, Robot.oi.rightJoystick));

This will return a command, which we can then pass to setDefaultCommand(), or tie to a button. The first argument is the subsystem that the command requires. The second is a lambda expression, which is the code to run in execute().

This still looks kinda long and messy. If we run it from within the DriveSubsystem class, we can make it considerably shorter:

QuickCommand.continuous(this, () -> drive.tankDrive(Robot.oi.leftJoystick, Robot.oi.rightJoystick));

While we can stick this code anywhere, it makes the most sense to put it in the subsystem that it belongs to. We'll make a method in DriveSubsystem, userTankDriveCmd(), that returns a command. The reason we make a method is so that we can pass parameters to the command. Although driving doesn't take parameters, other commands, such as PID setpoints, or extending/retracting an arm, do. The method looks like this:

public Command userTankDriveCmd() {
    return QuickCommand.continuous(this, () -> drive.tankDrive(Robot.oi.leftJoystick, Robot.oi.rightJoystick));
}

Now we can set this as the default command:

public void initDefaultCommand() {
    setDefaultCommand(userTankDriveCmd());
}

And that's it! Just a single-line method, instead of a whole class with six methods.

Exercise

Rewrite the userTankDriveCmd method so that it takes two Joystick parameters, left and right, and uses those for driving.

Further reading

  • (TODO) more about the QuickCommand class
  • (TODO) passing parameters
  • (TODO) oneShot vs. continuous