Flash3388 / FlashLib

A robotics development framework
BSD 3-Clause "New" or "Revised" License
10 stars 0 forks source link

Suggestion: Subsystem and System Interface #63

Open tomtzook opened 3 years ago

tomtzook commented 3 years ago

At the moment FlashLib supports the concept of system-oriented robot. A paradigm or the robot code is divided into classes, each describing a subsystem and has the ability to control its electronics. Then, actions can be made to operate those subsystems, leading to a good separation of logic and some abstraction.

The concept is sound, however the implementation of subsystem classes produces a certain problem. Consider the following subsystem:

public class Elevator extends Subsystem {

    private final SpeedController mMotor;

    public Elevator(SpeedController motor) {
        mMotor = motor;
    }

    public void up() {
        mMotor.set(0.5);
    }

    public void down() {
        mMotor.set(-0.3);
    }

    public void stop() {
        mMotor.stop();
    }
}

Now, in the robot class we can create it and then use it in actions. So what's wrong? Well we have a bunch of methods exposed from this class which we can only use in specific locations (like Action.execute or IterativeRobot.teleopPeriodic). The periodic requirements of them make them problematic in other locations.

Why is it a problem? Because it's not obvious that this is the case. Although it is easy to implement and use, it makes a confusing API:

mElevator.up() // bad
new UpAction(mElevator).start() // good

Proposal

What is so far known as subsystem is renamed to system interface (naming subject to changes), i.e. the class responsible for holding the electronic components for the system.

Subsystem: a wrapper for the system interface exposing actions that can be used instead of simple methods.

A system interface:

public class ElevatorInterface extends Subsystem {

    private final SpeedController mMotor;

    public Elevator(SpeedController motor) {
        mMotor = motor;
    }

    public void up() {
        mMotor.set(0.5);
    }

    public void down() {
        mMotor.set(-0.3);
    }

    public void stop() {
        mMotor.stop();
    }
}

A subsystem:

public class Elevator {

    private final ElevatorInterface mInterface;

    public Elevator(SpeedController motor) {
        mInterface = new ElevatorInterface(motor);
    }

    public Action up() {
        return new UpAction(mInterface);
    }

    public Action down() {
        return new DownAction(mInterface);
    }

    public void stop() {
        mInterface.cancelCurrentAction();
    }
}

And usage:

mElevator.up().start();
// or
mElevator.up()
    .withTimeout(Time.seconds(2))
    .andThen(mElevator.down())
    .start();

This new proposed API simplifies usage and provides better abstraction, more understandable API and is far more fluent to use. Plus, this is a simple user-side design modification, and thus retains some (if not complete) backwards-compatibility.

Unfortunately, some problems do present themselves: