Conditional Command Group?

We are trying to conditionally execute parts of our command group based on values read off of certain subsystems. However, we are unable to get access to any of the robot’s initialized subsystems in the command group or in OI (where the command group is being called from).

How do we execute certain commands based on values read from the robot?

1 Like

In the header file for the command group (like PickupAndPlace.h) , include Robot.h or whatever your main robot file is. This will give you access to all of your subsystems and oi or anything else in your robot class.

#include "../Robot.h"

You can put if statements around your addSequential/Parallel() calls so they will execute based on a condition, but this will only allow you to respond to values that are set when the robot starts. The initialization of the command group is only performed once when the robot starts, so if (for example) you want to respond to the value of a sensor when the command group is executed, this approach will not work.

Assuming you are following the CommandBased template and defining your buttons in OI you can’t conditionally run commands in a command group. Why? The command group is only ever constructed once and reused when ever the button is pressed.

If you are needing to do this you might need to rethink the design and structure of you code. We typically do the core of our control logic in state machines in the Subsystems and then uses commands to change the state.

A less elegant way is to code your commands with a short circuit path. See the example below. We used this on our 30 pt climber in 2013. If the shooter was up we couldn’t raise the arm as it would run into the shooter and cause damage. To prevent this if the RaiseArm command was called with the shooter up we finished the command instantly. It is important not to set a mechanism in motion in the initialize method or it will run regardless of the additional condition.


class RaiseArm : public CommandBase {
public:
	RaiseArm() {}
	void Initialize() {}
	void Execute() {
		arm->Raise();
	}
	bool IsFinished() {
		//Finish instantly if the shooter is up so the arm 
		//won't hit it.
		return arm->isUp() || shooter->isUp();
	}
	void End(){}
	void Interrupted() {}
};

If this solution is not adequate or too messy then perhaps you could post a specific example of what you are hoping to do in the command group.

I know I’m not OP, but I’m on his team - I’ll show you specifically what we are doing:

We have a command group like so:

Grp1::Grp1() {
AddSequential(new Cmd1());
AddSequential(new Cmd2());
AddSequential(new Cmd3());
}

Which is called by a button in OI.cpp:


groupButton->WhenPressed(new Grp1());

However to speed things up, we can skip Cmd2 in Grp1 occasionally based off the state of the robot. This kind of thing would be nice:

Grp1::Grp1() {
AddSequential(new Cmd1());
if (!mDrivetrain->canSkip()) {
   AddSequential(new Cmd2());
}
AddSequential(new Cmd3());
}

One of our mentors (that I think you know, Kyle :P) suggested we just make 2 separate command groups, and then have the button press call a new Command, which in turn checks the robot’s state and calls the appropriate group based off of that. I was just wondering if there are any cleaner solutions?

I didn’t even pay attention to the team number of the OP. But yes I do believe I know one of your mentors.

If I’m understanding why you want to skip a command correctly then personally I would code the command to finish instantly if it didn’t need to run. This is easier if you have sensors/feedback on all your mechanisms.

Ex.


class IntakeToteCommand: public CommandGroup
{
public:
	IntakeToteCommand(){
		AddSequential(new StackerGoToTopCommand());
		AddSequential(new IntakeTurnOn());

	}
};


class StackerGoToTopCommand: public CommandBase
{
public:
	StackerGoToTopCommand() : CommandBase() {
		Requires(stacker);
	}
	void Initialize(){
		if (!IsFinished()){
			CommandBase::stacker->SetPosition(15);			
		}
 	}
	void Execute() {}
	bool IsFinished(){
		return (CommandBase::stacker->GetPosition() > 15);
	}
	void End(){
		CommandBase::stacker->Disable();
	}
	void Interrupted(){
		End();
	}
};

Our stacker has to be at it’s top position to intake a tote. 99% of the time the Stacker is already at the top of the robot when we start to intake a tote. However we still fire off the StackerGoToTopCommand() as a safety precaution because if we don’t we won’t be able to pickup a tote. If it is already at the top then the command finishes instantly.

With this approach you can take everything into account that would ever need to occur for the given action to run without error. In doing so you can call the command group at anytime by itself and not have to think about wrapping the call of the command in additional logic.

I think I oversimplified our situation:

We actually wish to exclude a number of commands in this condition, some of which can (and should) be run in any and all states the robot can be in.

Having the command handle the logic still seems like the solution to your problem. You can write a new Cmd2IfSomeConditionExists command that wraps your original Cmd2 and only executes logic if the condition is true.

We’re also fans of state machines for more complicated scenarios.

Remembered another option for you here – one command can schedule another one.

See **How to schedule a command from within a running command **on https://wpilib.screenstepslive.com/s/4485/m/13810/l/277232-scheduling-commands for details.

I’m not sure how much you know about inheritance (maybe your mentor can help you), but you could create a class that extends Cmd2 and overrides the isFinished method to stop immediately if the conditions are met.