Page cover image

Code Organization

To use State Factory to the best of its ability and to make it easier for you, the programmer, to program, it is necessary to have good code organization.

Code organization is important for several reasons especially within FIRST where teams have multiple programmers and end up with very long files. What we recommend is a similar approach to the FTC Lib Command Based library: splitting subsystems into separate classes. This includes them having their own HardwareMap and having all methods relating to them within this class. A good approach to the implementation of this will be covered here.


To start, let's make an interface as a blueprint for what each class will contain.

public interface Subsystem {

}

Each subsystem will likely have something that needs to be updated every loop. This could be Tele-Op control, a PID algorithm, or really anything. These actions that must update every loop would be put inside of the update() method.

public interface Subsystem {
    void update();
}

Now, let's make a sample subsystem to show an implementation for this. The following code will be modeled off of a slides extension. In this, you might have it run off of 2 motors, so lets initialize those.

public class Slides implements Subsystem{
    DcMotor left;
    DcMotor right;

    public void Slides(HardwareMap map) {
        left = map.get(DcMotor.class, "leftIntake");
        right = map.get(DcMotor.class, "rightIntake");
        
        left.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
        right.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
    }
}

Next, to actually control the slides we might want to run a PID loop on every iteration.

public class Slides implements Subsystem{
    DcMotor left;
    DcMotor right;

    public void Slides(HardwareMap map) {
        left = map.get(DcMotor.class, "leftIntake");
        right = map.get(DcMotor.class, "rightIntake");
        
        left.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
        right.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
    }
    
    @Override
    public void update() {
        setPower(pid())
    }
    
    // setting motor power
    public void setPower(double pwr) {
        left.setPower(pwr);
        right.setPower(pwr);
    }
    
    public double pid() { 
        // Implementation not shown
    }
    
    // Other methods not shown
}

The idea here is that all of the methods that relate to this subsystem are placed here. To use this class in your main autonomous or Tele-Op, it's as simple as importing it, making an object, and then running methods off of that object.


Another way to organize your code is by abstracting tele-op controls into separate classes with an update method that would take gamepad input. Here is an example:

public class DriveControl {
    public Drivebase drivebase;
    
    public DriveControl(Drivebase drivebase) {
        this.drivebase = drivebase;
    }
    
    public void update(Gamepad g1, Gamepad g2) {
        drivebase.setPower(-g1.left_stick_y, -g1.left_stick_x, -g1.right_stick_x);
    }    
}    

Abstracting code to classes can be extremely useful in organizing segments of code that may be used multiple times. It also cleans many lines of code that may otherwise clutter your programs.


Now, we can put this together in our op mode. For this example I'll create a TeleOp using a LinearOpMode.

@TeleOp
public class SlideTester extends LinearOpMode {

    @Override
    public void runOpMode() throws InterruptedException {
        Slides slideSubsystem = new Slides(hardwareMap);
        Drivebase driveSubsystem = new Drivebase(hardwareMap);
        DriveControl driveControl = new DriveControl();
        
        Subsystem[] subsystems = new Subsystem[]{slideSubsystem, driveSubsystem};
        
        waitForStart();
        
        while(opModeIsActive()) {
            for(Subsystem system : subsystems) system.update();
            driveControl.update(gamepad1, gamepad2);
        }
    }
}

In this class, the Slides and Drivebase is being created with the hardwareMap object passed in. By instantiating these objects, the proper hardware map actions would be executed. Then, in the opModeIsActive() loop, the control classes are being updated. By updating them every loop, they are being updated on the status of the gamepad.

Last updated