We are having a problem regarding making a toggle method for the button class. In the OI file, we plan to code certain buttons such that the first time its pressed one command would execute, the next time a different command would, and it would alternate between (thus toggling between these two states).
One of the powerpoints we came across described going into the Button source code file and adding a new method, so we wrote a new one that accepts two commands as the parameters and have worked out the logic for the toggle. It doesn’t seem to be working, so could someone explain the proper way to code it? Thank you very much.
*In order to toggle, you first have to detect the rising edge of your joystick button (because you want it to toggle only once when you press the button, not repeatedly while you’re holding the button down).
Then you toggle a boolean each time a rising edge is detected. Then use the value in that boolean to determine what action to take.
Changing WPILib’s code is a bit risky, I wouldn’t do that. You could, however, write some classes of your own that demonstrate similar principles. What I would do is define a “ToggledPressedButtonScheduler” class:
Are the two commands related in any way? (i.e., could you use one command for both actions?). If so, you may want to consider refactoring either your subsystem or command code so that you have this functionality.
OR, you could put them all inside a command group with a bool that holds state, and based on that run either of two commands. Just make sure your “state” variable inside of whatever subsystem is actually being toggled back to whatever is needs to be.
//In the CommandGroup foo, you could either have a state that is toggled from within one of your subsystems,
//ala GetterSetter pattern and use this state to make a decision.
//OI code
JoystickButton *xyz = new JoystickButton(stick1, 11);
xyz->WhenPressed(new foo());
//CommandGroup 'foo' code
subsystemOne.ChangeStateOfFooBar();
if(subsystemOne.GetStateOfFooBar())
{
AddSequential(new bar());
}
else
{
AddSequential(new baz());
}
Don’t forget that the calls to “AddSequential” should be in the CommandGroup’s constructor, which means they run only when an instance is created (probably when the code first runs). The condition you put there will not have the effect you expect it to have. The created CommandGroup will always run either “bar” or “baz”, depending on the value “GetStateOfFooBar” returned when the constructor was called.
That means one DoStuff instance is created, and that instance is passed to the “WhenPressed” function. It will be that same instance that runs every time the button is pressed. I don’t see how this code will cause a new instance to be created every time the button is pressed.
Uh… no, sorry, that’s not how it works. If you look at the documentation, you’ll see the following line:
void WhenActive (Command *command)
That means the function receives a pointer pointing at a Command object. When you write:
new DoStuff()
The type you get from that expression is a pointer to a DoStuff object (the one you just created). If you can point me to any piece of code that suggests it somehow uses that object to create more objects of that kind, please do.
Looking deeper into the code, you can see that instance ends up being stored in an instance of “PressedButtonScheduler”, which takes it upon itself to check the state of the button and start the command instance if necessary (not create another instance).
So then would the OP be able to use a Command, but not a CommandGroup, and inside the Initialize() method get the state of whatever subsystem and run other commands from inside the Execute or Initialize methods?
Sorry, I did not look carefully and did not realize all of these happened in the constructor (I have been passing in values to command’s constructors to have them do two different things during runtime, although all of that logic was in the subsystem’s functions, not the command.)
We originally did something very much like this final solution. Then we did something more like the command group idea:
Currently we have commands for Do, Undo and Toggle. On Toggle we ask the subsystem what the state is, then do a Start() on the appropriate Do or Undo command to go the other way.
We did this because we want the command to indicate which operation is ongoing (Do or Undo) for such reasons as display on the SmartDashboard and logging.