Quote:
Originally Posted by DjScribbles
• Concurrent commands that have complex interactions between each other get complicated fast, to the point that it seems easier to 'cheat'
|
I think this is an issue of Command design, not of the overall model. If there are two Commands that interact in weird ways if used improperly, I try to contain their use cases in CommandGroups that can guarantee that they will interact as desired. This isn't always feasible, but it can be much more readable than a complex redesign to handle the weirdness.
Quote:
Originally Posted by DjScribbles
• When a command finishes, if you assign a default command, it takes over immediately, which can get tricky when you build a sequence of commands (where each must finish for the next to execute)
|
If you're using a CommandGroup, this isn't a problem. CommandGroups implicitly require() all subsystems that the Commands in them require. This year our launcher requires some pretty complex sequences, but by using CommandGroups and setting one as the default command, we were able to automate all but a very small part of it.
Quote:
Originally Posted by DjScribbles
• For us, in order to spin the shooter wheels and fire, you need them both to be different subsystems (without cheating in your commands, or making a goofy "KeepSpinningAndShoot" command), even though they are functionally tightly coupled; because a command that requires a subsystem will interupt the current command that is executing on that subsystem when it runs.
|
This is a situation where running the wheel control in a different thread (eg. through a PIDController/PIDSubsystem) would be helpful. Last year, our Shoot command looked like this:
Code:
addSequential(new SpinUp(speed));
addSequential(new DeployDisk());
While this ran, even after SpinUp ended, the wheel kept spinning. Using a PIDSubsystem also let us add a safety feature: the default command waited 5 seconds, then spun the wheel down, in case it hadn't been done manually.
Quote:
Originally Posted by DjScribbles
• Tons of commands leads to a lot of small, similar files. Sometimes updating the boiler plate in all instances can be quite cumbersome.
|
If you are suffering from a lot of boiler-plate, make some classes to inherit from! For example, our code-base includes this class:
Code:
// Does nothing, and keeps doing it forever
public class DoNothing extends Command {
public DoNothing() {}
protected void initialize() {}
protected void execute() {}
protected boolean isFinished() {
return false;
}
protected void end() {}
protected void interrupted() {}
}
Quote:
Originally Posted by DjScribbles
• Figuring out the right way to do things when a necessary feature isn't supported by robot builder can be interesting. For example adding a Camera subsytem as an empty subsystem in RobotBuilder, then manually adding the camera outside the auto-generated regions; or to create a Counter class, adding digital inputs to robot builder, then using those to initialize the counter class rather than using the channels directly.
|
This is one of the reasons my team does not use RobotBuilder. It's very good for getting some example code or a base to start from, but as soon as you try to modify anything in a way that the template's writer didn't think of, you're somewhat screwed. Creating the subsystems and declaring all the hardware by hand is not an arduous task, and reduces barriers to any necessary changes.