ConditionalCommand and Combined Trigger

In order to control the cargo collecting mechanism we have 2 commands PrepareToCollectCommand and StopCollectingCommand.

Binding this commands to 2 different buttons works like a charm:

// Activate the intake Mecanism
(new JoystickButton(m_gameController, Button.kY.value))
     .whenPressed(new PrepareToCollectCommand(m_intake, m_storage));

// Deactivate the intake Mechanism
(new JoystickButton(m_gameController, Button.kB.value))
     .whenPressed(new StopCollectingCommand(m_intake, m_storage));

Now we want to have those commands used within a toggle controlled by 2 simultaneous buttons on the flight sitck

    // If both the grip button and trigger are pressed then toggle the intake
    new JoystickButton(m_flightJoystick, 1 )
    .and(new JoystickButton(m_flightJoystick, 2))
    .whenActive(new ConditionalCommand(
        new StopCollectingCommand(m_intake, m_storage), 
        new PrepareToCollectCommand(m_intake, m_storage), m_intake::isActive));

For some reason the Activation works (on False) but the deactivation (on True) is never triggered.
isActive is returning the internal state “m_activated” of the subsystem which is set to true when activated and false when deactivated (state changes using the 2 bindings as shown before)

What are we doing wrong?

I can’t see a problem here. Can you share more of the code, perhaps a Github link?

An alternative way to implement this would be to have a CollectCommand that keeps running while your intake is active. It does whatever PrepareToCollectCommand does in the initialize, and whatever StopCollectingCommand does in the end. Attach it to your triggers with toggleWhenActive​.

Maybe it’s just my style, but it seems better practice to have a command running to reflect the fact that motors are running.

So do you want to have it bound to two different buttons?

What are you attempting to do here? Bind it to two buttons, bind it to a single button?

I would recommend binding the stop command to JoystickButton.whenReleased()
JoystickButton is a child class of Button, so you would have to go to the docs for the Button class to see the exact specifications of this method.
the code would look similar to this:

new JoystickButton(insert required parameters)
.whenPressed(start);
new JoystickButton(same button as before)
.whenReleased(stop);

https://github.com/team8626/team8626_2022.git

It’s in RobotContainer, line 126

Did you make sure to intake (or otherwise fill your storage subsystem) when you tested the command? It looks like that the PrepareToCollectCommand only stops when that happens, and I don’t see any other problems.

We did our first completion last week with a complete manual control of the subsystems on a gamepad (one button for intake down, one button for intake up, gamepad joysticks to move the ball inside the storage, one button to start shooter, one button to stop shooter) while the driver had the stick to control the drivetrain.
Now that all subsystem kind of work the way we want we are trying to automatize some of the functions of the subsystems.

The feedback from the driver is that there would be less chance of banging the intake in others/walls if he had the control of deploying/retracting the intake. And he said using a double button trigger would prevent deploying by mistake. This is what we are trying to achieve.

Just to be clear, you don’t want to instantiate this twice.

button = new JoystickButton(controller, number)
button.whenPressed(new startcommand)
button.whenReleased(new stopcommand)

Same idea as above, but just making sure it’s clear enough what is happening there.

PrepareToCollect adds 2 parallel commands:

new InstantCommand(m_intake::activate, m_intake)
and
new StoreCargoCommand(m_storage));

Since the command on intake is an instant comment, it terminates immediately so we should be able to act again on intake regardless of the storage status (in the isFinished method), shouldn’t we?

I agree with your drivers, and this is exactly what we do as well.

We dual the whenPressed() and whenReleased() on the same button as described previously.

The whenPressed() command we use is actually a sequential command group that drops the intake arm down, and then starts the intake/indexing of balls automatically. That sequential group runs until our indexer is full, or until the driver releases the button.

Given that only the driver knows what direction they want to move, it saves the robot a lot of repairs if they are also responsible for protecting the vulnerable intake.

EDIT to add link.

Thanks, we might go to the whenPressed()/whenRelease() option and keep it simple until we have time to understand why it didn’t work the way we were planning on doing it!

As far as I know, whenActive schedules a ConditionalCommand (which is PrepareToCollect) first, and then it schedules another ConditionalCommand (which is StopCollecting). Since the first command that was scheduled hasn’t ended, the second command isn’t run. You could use whileActiveContinuous, which interrupts the command when the button is stopped being held.

Thanks.

In many of your commands (PrepareToCollectCommand.end,StoreCargoCommand constructor, StoreCargoCommand.end ) you create other commands and immediately discard them without any attempt to schedule them. Generally a command will never create another command. (The exception is for a command group, where they are being passed to addCommands in the constructor.) Instead you should be calling subsystem methods (in initialize and end, never in the constructor).

@Override
public void end(boolean interrupted) {
  // Deactivate the Intake
  new InstantCommand(m_intake::deactivate, m_intake); // HAS NO EFFECT!
}

public StoreCargoCommand(StorageSubsystem storage) {
  m_storage = storage;

  new LoadStorageUnitCommand(m_storage.getFrontUnit()); // HAS NO EFFECT !
  addRequirements(m_storage);
}

public void end(boolean interrupted) {
  // Force stop of the storage units
  // NEXT TWO LINES HAVE NO EFFECT !
  new InstantCommand(m_storage.getFrontUnit()::stop, m_storage.getFrontUnit()); 
  new InstantCommand(m_storage.getBackUnit()::stop, m_storage.getBackUnit()); 
}

In PrepareToCollectCommand, you override isFinished. Generally you should not override that method on command groups (only on subclasses of CommandBase), especially as you’re just replicating the code from one of the commands in the group.

Again, I think your code would be simpler if you simply had one “intake deployed and running” command and didn’t try to keep track of state. You can still make the storage unit refuse to run when full without ending the command.

Also, you’ll find it much easier to debug this stuff if you put your subsystem internal state onto SmartDashboard/ShuffleBoard. Booleans can be shown as large red/green squares.

1 Like

Yup… working on that for all subsystems!

Could you make the stop collecting command the default command for the subsystem and just you toggleWhenPressed to activate the prpare to collect. Then you hit the button again to end that command and it would go back to the default of stop collecting?