Can default commands be shared? I’m trying to use one command to read values from NetworkTables and control multiple subsystems. Is it allowed? And if so, how would I go about doing it?
I’ve tried doing it like so:
driveTrain = new DriveTrain();
elevator = new Elevator();
intake = new Intake();
defaultCommand = new SomeCommand();
driveTrain.setDefaultCommand(defaultCommand);
elevator.setDefaultCommand(defaultCommand);
intake.setDefaultCommand(defaultCommand);
My gut feeling is that’s not possible due to how WPILib has been architected. Each command will need a requires() to set up the mutexes and other concurrency constructs to ensure a subsystem is only executing a single command at a time. That being said:
I’m not a big fan of the command-based paradigm and the last time I’ve written command-based robot code was in 2014.
Can you elaborate on what you mean by “doesn’t work?”
It would be nice if you had a bit more substance to your example - what you’ve provided is not something I could use on my end to reproduce whatever issue you’re having.
Are you calling requires() in the constructor of SomeCommand()?
When I tried it, the command itself doesn’t seem to be running. I can’t be sure that it’s caused by the default command problem, but I have a feeling it’s related.
I am calling requires to all subsystems in the constructor. The snippet of code I posted is from inside robotInit. I also left the initDefaultCommand of my subsystems blank since it will be set in robotInit anyways.
This isn’t much of a problem as it is a curiousity. I can very easily make separate default commands for all 3 subsystems, or merge all subsystems into one. However I cannot find any sources online stating whether this is possible.
I’ve never been a lead programmer, but we’ve used command-based programming from our very first year. My understanding of “default command” is that’s it’s a command that runs when a **specific **subsystem is otherwise unoccupied. I’m not sure exactly what you mean to accomplish by a “multi-subsystem default command” that can’t be accomplished by a default command for each subsystem. As 3946 has written subsystems and commands for them, I can’t think of any cases where this paradigm would be useful; can you be more clear on WHY you want this?
Edit: Also, what happens if some, but not all, of the subsystems are available. Should they adopt the “global default” command even if other subsystems are unavailable?
Well… As I said, I could very easily make separate default commands for each one, or just merge all my subsystems into one (since no other command will run on them in this simple project). It’s more of a curiousity of whether it’s possible. As for my situation, I have 3 separate subsystems (copy and pasted from our actual competition code), and for my simple task I want to use one command to control them all, since it would be a bit overkill to make separate commands each with basically 2 lines. Yes, there are many ways to avoid doing it, but I’m just curious as to whether if you CAN do it.
Not sure what you mean here. Are you speaking specifically to the OP’s question, or to command-based stuff in general?
I think that this would be the best option if you’d like to stay within the command-based school of thought. However, there’s still an opportunity to modularize; for example, you might consider having an abstract command class that implements the logic and three separate command classes that inherit and call the proper requires().
If you’d prefer to not do that, I’d suggest using TimedRobot for your main class.
Without a doubt, this goes against the CommandBased Paradigm. Any given command should generally only require one subsystem, and CommandGroups should chain commands to perform actions involving multiple subsystems.
However, that aside, this wouldn’t really work, especially if you are requiring all three subsystems in the default command. This is especially the case if you did have other commands you wanted to call for those subsystems. As soon as any of the three subsystems had no command requiring it, the shared default command would trigger, requiring all three subsystems, this would cancel any other commands that might be using either of the other two subsystems (unless that other command was set to uninterruptible).
I don’t even want to think about what kind of weirdness could happen if each subsystem had a separate instance of the command. If WPILIB doesn’t procedurally re-check each command as it goes launching defaults, you could get each one triggering, and cancelling each other.
On the other hand, I have thought of a few cases were something like this could be handy.
If you had multiple identical subsystems, say multiple turrets with t-shirt cannons on them, that you wanted to control seperatly, it could make sense to create commands where you could pass in the instance it requires in the constructor. You’d still want each subsystem to initiate its own instance of that command, but it could work.
If you wanted a global “STOP” command that all, or numerous different subsystems defaulted to you could create a command that will run a stop() method on whatever subsystem is, similarly to before, passed into the constructor. and each subsystem’s stop method handles the rest. Again, you’d want each command to have its own instance of that stop command, but it would also make sense in this case to share the command, if you make it properly generic.
Thanks for the explanation. I see what you mean… But i still have a question:
Any given command should generally only require one subsystem, and CommandGroups should chain commands to perform actions involving multiple subsystems.
I’ve never heard of this saying before… I have written lots of commands that require more than one subsystem, and they all worked fine. So if I was to have a composite action, such as moving forward and running the intake at the same time, I should make separate commands and put them in one CommandGroup with addParallel? The idea that commands should generally require one subsystem sounds right, but in a situation like this it seems way overkill to make 3 instead of 1.
I do not not mean that you can only have one subsystem per command, WPILIB explicitly supports multiple requirements in a single command, but in general, good programming practice emphasises code modularity and the separation of logic. Often quoted as cohesion and coupling in computer science classes.
I find that such an approach also follows the Unix philosophy of do one thing, and do it well. If I need to do two things at once, I would rather write two commands, each of which does its one thing correctly and plainly, and then combine them with a CommandGroup because this separates the logic and makes it easier for me to go back and find bugs if one part isn’t performing. Also, if I later need to move forward while doing another task (like say lifting your intake to place in a high goal) or some other combination of tasks that I have already written before, you can reuse your existing command.
An example where a command will need two subsystems which does not work as easily with CommandGroup would be an “acquireGamePiece()” which requires fine control over both an intake and the drive train. Something like this should never be a default command, only something triggered by a specific action or state.
A default command with multiple subsystems has too many ways it can do things you probably don’t want, e.g.:
The intake has a default command, intakeDefault(), which also requires use of the arm. While intakeDefault() is running, the driver pushes a button which causes raiseArm() to begin, but does not specify any command for the intake. As the arm subsystem has been co-opted, the intakeDefault() command exits. Now, the intake is not running a command, so it again starts up intakeDefault(). This requires the arm subsystem, so raiseArm() gets canceled.
The idea that commands should generally require one subsystem sounds right, but in a situation like this it seems way overkill to make 3 instead of 1.
Repeating yourself three times isn’t too bad - and if you’re going to be sharing a lot of the logic, you can put that code in a fourth place that’s called by the three commands you’re going to make. That was the basis of my comment about modularity earlier in the thread.
The way the OP interpreted you originally may actually be closer to the truth. My guess is that Command supporting multiple requires() has more to do with the fact that CommandGroup inherits from Command, and less to do with someone deciding that this was an intentional use case. In other words, I bet someone was thinking it was “easier” to put all the logic there and did not fully consider the semantics of that decision.
…in general, good programming practice emphasises code modularity and the separation of logic. Often quoted as cohesion and coupling in computer science classes.
There’s a difference between mere programming and software engineering in my mind. I think that the latter is harder to learn, because it requires good opinions. And those are formed only after a lot of exposure, failure, debugging, and analysis.
One should definitely prefer CommandGroups to Commands for actions that require multiple subsystems, but there are cases in which interaction is required between the states of the two subsystems in ways that CommandGroup does not really facilitate. Do not treat heuristics as hard rules.