Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   Programming (http://www.chiefdelphi.com/forums/forumdisplay.php?f=51)
-   -   Making more reusable commands (http://www.chiefdelphi.com/forums/showthread.php?t=123920)

Joe Ross 03-01-2014 11:59

Making more reusable commands
 
Last year, we used Java with the command framework. I was very happy with how it ended up, our code was the best organized and easiest to modify that it's ever been. We used RobotBuilder, which reduced much of the tediousness of creating subsystems. However, we ended up with over 60 commands.

We did end up with a lot of very similar commands, that could be abstracted to the following:

turning on a motor at a fixed speed
turning off the motor
extending a pneumatic solenoid
retracting a pneumatic solenoid
toggling a pneumatic solenoid.

For each of these, we made methods in the subsystem for a particular motor or a particular solenoid that translated a forward or reverse on the solenoid to an extend or retract (which depended on which way the solenoid was wired and plumbed).

I'd like to be able to reduce the number of very similar commands and subsystem methods

I had a few thoughts, but none of us are very experienced with OO design patterns, so I'm hoping someone has a better way.

1) create generic commands, and make the solenoid/motor in the subsystem public, and pass the solenoid/motor to the command. This is probably the simplest, but I lose the ability to easily translate a forward/reverse to an extend/retract in one place.

2) do #1, but extend the solenoid class to have a parameter to determine whether forward is extend or retract. This solves #1s problem, but then it makes it much harder to use robot builder

3) make a solenoid interface for extend/retract, and have the subsystem implement the interface. Then you could pass the subsystem to a generic command. However, you would be limited to a single solenoid per subsystem.

Does anyone have other ideas? What did your team do?

Kevin Sevcik 03-01-2014 13:09

Re: Making more reusable commands
 
You can make commands generic-ish by passing parameters to a parameterized constructor, then having that command pass that parameter to a parameterized method of your subsystem. I'll type up a quick c++ example in a bit here and post it.

You might be able to pass a subsystem as a parameter, but i don't know off the top off my head how that would play with the "requires" functionality and all that.

Andrew Lobos 03-01-2014 13:16

Re: Making more reusable commands
 
Quote:

Originally Posted by Kevin Sevcik (Post 1319809)
You can make commands generic-ish by passing parameters to a parameterized constructor, then having that command pass that parameter to a parameterized method of your subsystem. I'll type up a quick c++ example in a bit here and post it.

This is what we did in Java last year. There where generic commands like "SetFlywheel" that took parameters from the constructor and used them in the calls to the subsystem. These generic commands were implemented in CommandGroups that did specific tasks, like FarShot3 for full court 3pt.

Code is here if you'd like to take a look: https://github.com/FRCTeam225/2013Of...rc/org/team225

Tom Bottiglieri 03-01-2014 13:50

Re: Making more reusable commands
 
We (on 254) usually just end up writing a bunch of boiler plate code to do this, but I think #3 is a solid way of solving this. Personally I don't make an abstraction until I have at least 3 things that need it (you know the whole bit about premature optimization...). If I had to, I'd make Subsystems implement Interfaces like "Extendable" or "Controllable" then make commands that handle that.

125 did this last year a little differently by subclassing subsystem, then building Commands that could control those types of subsystems.
https://github.com/kreeve/Nutrons201...Subsystem.java

Tom Bottiglieri 03-01-2014 13:52

Re: Making more reusable commands
 
Quote:

Originally Posted by Joe Ross (Post 1319781)
3) make a solenoid interface for extend/retract, and have the subsystem implement the interface. Then you could pass the subsystem to a generic command. However, you would be limited to a single solenoid per subsystem.

Build an interface that requires extend() and retract() methods, have the subsystem use it, then do whatever you need in the implementation of those methods

Alan Anderson 03-01-2014 14:22

Re: Making more reusable commands
 
If you're worried about writing commands to control individual cylinders, you're probably not at the appropriately high level of abstraction. I suggest that you should be controlling subsystems, not actuators. The subsystem implementation should take care of the details of motor and relay and solenoid control based on commands for speed and state.

Don't extend or retract a cylinder. Instead, engage or disengage a brake, or lock or unlock a latch, or open or close a gripper. It would be appropriate to extend or retract an arm, if that's the motion you've designed it to do.

yash101 03-01-2014 14:57

Re: Making more reusable commands
 
I really think that it would be nice to make some sort of Library to set up everything for you. That way, could have code like:

int main() {
init victor("shooter");
setSpeed("shooter", 127);
return 0;
}

And yes, that was probably incorrect C syntax.

Alan Anderson 03-01-2014 15:14

Re: Making more reusable commands
 
Quote:

Originally Posted by yash101 (Post 1319836)
I really think that it would be nice to make some sort of Library to set up everything for you.

That is what the RobotBuilder tool is for.

notmattlythgoe 03-01-2014 15:35

Re: Making more reusable commands
 
Quote:

Originally Posted by Alan Anderson (Post 1319829)
If you're worried about writing commands to control individual cylinders, you're probably not at the appropriately high level of abstraction. I suggest that you should be controlling subsystems, not actuators. The subsystem implementation should take care of the details of motor and relay and solenoid control based on commands for speed and state.

Don't extend or retract a cylinder. Instead, engage or disengage a brake, or lock or unlock a latch, or open or close a gripper. It would be appropriate to extend or retract an arm, if that's the motion you've designed it to do.

This is the way it should be done. The parts (solenoids, motor controllers, encoders, etc.) should be contained to each of the different subsystems and how you control those different parts should be strictly limited to the methods you make available to the commands. Commands should never have direct access to any of the devices set up in a subsystem.

Kevin Sevcik 03-01-2014 16:20

Re: Making more reusable commands
 
Quote:

Originally Posted by notmattlythgoe (Post 1319847)
This is the way it should be done. The parts (solenoids, motor controllers, encoders, etc.) should be contained to each of the different subsystems and how you control those different parts should be strictly limited to the methods you make available to the commands. Commands should never have direct access to any of the devices set up in a subsystem.

Agreed, but there's still value in parameterized Commands for things like 4ndr3wl is talking about. We did the same last year so we could have a Shoot command that we passed an enum to pick which set of speeds+angle we were firing at. As opposed to having a ShootFar, ShootNear, etc. command. Which means if we needed a new set of speeds+angle, we could just add another element to the array and another constant to the enum. Instead of adding yet another command.

notmattlythgoe 03-01-2014 16:22

Re: Making more reusable commands
 
Quote:

Originally Posted by Kevin Sevcik (Post 1319877)
Agreed, but there's still value in parameterized Commands for things like 4ndr3wl is talking about. We did the same last year so we could have a Shoot command that we passed an enum to pick which set of speeds+angle we were firing at. As opposed to having a ShootFar, ShootNear, etc. command. Which means if we needed a new set of speeds+angle, we could just add another element to the array and another constant to the enum. Instead of adding yet another command.

Completely agree, we use a drive straight command that takes a power and a distance so we can reuse it.

Jefferson 03-01-2014 17:20

Re: Making more reusable commands
 
We were fully command-based last year except for solenoids. The vast majority of the time, we extend/retract solenoids based on a single button push making simple if/else statements in the TeleopPeriodic function the easiest to implement and modify.

Fifthparallel 03-01-2014 17:26

Re: Making more reusable commands
 
You need to abstract business logic (interactions between subsystems, etc) away from functionality -- i.e. have business logic executed inside your commands and actually functionality (like extend/retract for a piston) be part of an API that gets thrown into that subsystem. Our 2013 C++ code isn't the best example, but it does have some examples -- https://github.com/FRC-Team-1410/UA2013-FRC1410-Robot

notmattlythgoe 03-01-2014 17:34

Re: Making more reusable commands
 
Quote:

Originally Posted by Jefferson (Post 1319889)
We were fully command-based last year except for solenoids. The vast majority of the time, we extend/retract solenoids based on a single button push making simple if/else statements in the TeleopPeriodic function the easiest to implement and modify.

I guess my question is, why not design the systems the solenoids use as a subsystem? We had subsystems this year that were just a single solenoid (trigger, shooter elevation, hopper, and pickup arm). We like to separate the solenoids into subsystems because we feel that it makes the project easier to read and practices good OOD technique by locking those devices into a subsystem that only a command requiring that subsystem is allowed access to control.

Joe Ross 03-01-2014 18:12

Re: Making more reusable commands
 
Quote:

Originally Posted by Alan Anderson (Post 1319829)
If you're worried about writing commands to control individual cylinders, you're probably not at the appropriately high level of abstraction. I suggest that you should be controlling subsystems, not actuators. The subsystem implementation should take care of the details of motor and relay and solenoid control based on commands for speed and state.

That is exactly what we did last year. However, after writing the same code in a subsystem for the 6th solenoid and the 3rd on/off motor, it gets kind of old. For easy things like a solenoid or on/off motor, I'm not against giving up a little bit of self documenting code, if it makes writing the code easier and less error prone.

Quote:

Originally Posted by Kevin Sevcik (Post 1319809)
You can make commands generic-ish by passing parameters to a parameterized constructor, then having that command pass that parameter to a parameterized method of your subsystem. I'll type up a quick c++ example in a bit here and post it.

I'm now thinking about a generic "super" command that you can pass a solenoid to. Then make a bunch of commands that extends the "super" command. This commands would be named appropriately per the function and would pass the appropriate solenoid or motor in the constructor and any other parameters, and not need any more code.


All times are GMT -5. The time now is 03:35.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi