Command.cancel() question

I have a SequentialCommandGroup containing several commands and a ParallelCommandGroup. If I call the cancel() method on the SequentialCommandGroup, I expected all of the comands in that group, including the Parallel group to be canceled. This is not happening. My execution trace shows the commands in the two groups continue to run after the cancel() call on the Sequential group. Reading the WPILib code for commnds and command groups it does not appear cancel on a group cancels the contained commands. What is the expected behavior of cancel() on command groups?

(assuming you are using Java)

not from wpilib but FWIW, taking a quick look through the CommandScheduler.cancel(), SequentialCommandGroup.end() and ParallelCommandGroup.end() methods, it looks like the expected behavior should be that any currently running command (in the sequential group) will be properly end’ed, and the ParallelCommandGroup.end() appropriately end’s all running commands

are you doing anything special inside your command groups (i.e. like scheduling other commands directly?) - theres no way the scheduler would know about those so you would have to end them yourself

Is the SequentialCommandGroup you are cancelling contained inside another command group? If so, cancel will not work on it. This is from the CommandScheduler.cancel() method…

      if (!isScheduled(command)) {
        continue;
      }

It will not cancel commands that are not scheduled. For a command group, only the outermost group that is scheduled is known to the command scheduler. The lifecycle of the contained commands are managed by the command group according to the group’s semantics.

For a command inside a group to end (independent of the whole group ending), its isFinished() method has to return true.

1 Like

Perhaps more information would help. I have a command, lets call it C. In that command I create a command group, lets call it CG. In the constructor of C, CG is created and a series of other commands (including a ParallelCommandGroup PG) are added to CG. Later when C is scheduled, in it’s initialize function, I call CG.schedule(). In the C.end() function, if interrupted is true, I call CG.cancel() expecting all commands in CG to end. C.isFinished watches CG.isScheduled() to determine when all the commands in CG have completed.

This works in that the robot behaves as expected. However, an unexpected driver action led to an unexpected robot behavior which led me to increase tracing and look at C’s execution in detail. What I found is C had a subsystem required that is also required in one of the commands added to CG. At scheduling, this causes C to be interrupted immediately. I see C.end(true) being called, this calls CG.cancel(). However, the commands in CG continue to run to completion of the command group.

So with the required subsystem error present, the cancel did not kill the command group. If it had, we would have found this issue last season. But, because the command group continued to execute, the robot did what was expected despite the error and we were none the wiser until now.

I think the issue is that schedule cancels scheduled commands with conflicting requirements before the new command becomes scheduled. This means that C.end() is called before CG.isScheduled() returns true and CG.cancel() therefore has no effect. This behaviour by the scheduler is necessary in order to preserve the invariant that two commands with the same requirement are not scheduled at the same time.

A simple fix would be to remove the subsystem from C. A more complex fix would be to reorganise the code so that it does not call schedule and cancel. This is a little under-tested, but I sketched out a possible approach for creating commands on initialize.

Thanks for that, it makes sense. I will work on this next time I am at the shop.

Diagnosis is correct. The command C was being interrupted (due to requirements bug) before the group CG’s commands were scheduled, so .cancel had no effect. Fixing the requirements bug allows C to be around for the duration of the group and if C is interrupted after group is running, the cancel works as expected cancelling the whole group.

Code is working as expected now.