Commands are an interface, anything that implements the interface works as a command, but in Mercurial implementing it by hand is rarely done.

Commands have the following components:

Initialise

This runs when the command starts up. Its good to put run once or setup tasks in here. For instance:

  • Setting a target position
  • Logging that the command is starting
  • Setting a motor power
  • Storing a starting state
  • Enabling another system (e.g. a Dairy Controller)
  • Disabling another system (e.g. a Dairy Controller)

Execute

This may run multiple times, and is guaranteed to run at least once, straight after initialise. Its good to put code that needs to run an update here. For instance:

  • Updating a PID controller or similar
  • Setting a motor power based on a changing input
  • Updating internal state used to determine the finish condition

End

This runs when the command ends, and gets told if the command was interrupted or not. A command is interrupted if it comes to an end that was not due to its finish or, some command group utilities will end a command early but not interrupt it. We’ll look more at interrupting later. Its good to put cleanup tasks here. For instance:

  • Setting a target position
  • Logging that the command is ending
  • Setting a motor power
  • Enabling another system (e.g. a Dairy Controller)
  • Disabling another system (e.g. a Dairy Controller)

The Contract

These three parts of a command are the most important. Commands are guaranteed to run these three in order, with execute potentially being called many times. This behaviour is unit tested in Mercurial, and all official command groups and the scheduler are guaranteed to run commands like this, so if you write your own group, you must adhere to this rule.

Finished is also important, it often runs after execute, but is not guaranteed to be run.

Finished

This may run multiple times, but is not guaranteed to, and is used by the command scheduler and command groups to determine if a command is done.

A command should never rely on finished being run to function correctly, for this reason, finished should not modify state, it should only check state to perform the boolean check.

For instance:

  • Is the encoder at the target position?
  • Has enough time elapsed?
  • Is the motor overcurrent?
  • Has the sensor been triggered?

Requirements

The set of requirements. Requirements are ‘lock’ objects for commands, and commands use them to declare things that they need exclusive access to. If a command that has overlapping requirements with a currently scheduled command is scheduled, the currently scheduled command is checked to see if it can be interrupted, and if so, it is cancelled early.

Command groups don’t look closely at requirements, so its assumed that the user will do the right job there, but they do compute and report the union set of all requirements of all commands they contain.

Mercurial doesn’t require that subsystems are used as requirements, but usually they are, or hardware objects are used, as both of these are commonly needed for exclusive access.

Although this is accessed via a getter, its assumed that the contents don’t change for a command.

RunStates

Run states are the states that a command is allowed to be scheduled in.

Most commands should not be able to be ran in Init, so this prevents that.

However, if a command has runstates of OpModeState.INIT, and it is started in this OpModeState, and then it becomes OpModeState.ACTIVE, the command will not finish prematurely.

Same as Requirements, this should not change.

Interruptible

Also see Requirements. If a command is interruptible, that means that if another command wishes to be scheduled, and there is a conflict of requirements, then this should end early, and interrupted.

Schedule and Cancel

All commands can be scheduled and cancelled via Command.schedule() and Command.cancel() these methods are the only ways to do so.

If you override these methods, you must include the super method from the interface.

Names and Stacktraces

Commands need to have a string representation, and a stack trace unwind representation.

Don’t worry too much about this if you are writing your own Command class that isn’t some group. However, if you are writing a group of some description, the CommandGroup abstract class may be helpful, and will handle this automatically.

Command.toString() must return the name of the command, learn more about command names and error messages in the next section.

Composition

Commands all have decorator methods to easily compose them into the majority of built in command groups.

We’ll look at those when looking at the relevant command groups.