Grupos de comandos

Los comandos individuales son capaces de realizar una gran variedad de tareas de robot, pero el formato simple de tres estados puede volverse engorroso rápidamente cuando se requiere una funcionalidad más avanzada que requiere secuencias extendidas de tareas de robot o la coordinación de múltiples subsistemas de robot. Para lograr esto, se anima a los usuarios a utilizar la poderosa funcionalidad del grupo de comandos incluida en la biblioteca basada en comandos.

Como sugiere su nombre, los grupos de comandos son combinaciones de múltiples comandos. El acto de combinar varios objetos (como comandos) en un objeto más grande se conoce como composición <https://en.wikipedia.org/wiki/Object_composition> __. Los grupos de comandos componen múltiples comandos en un comando compuesto. Esto permite que el código se mantenga mucho más limpio y simple, ya que los comandos componentes individuales pueden escribirse independientemente del código que los combina, reduciendo en gran medida la cantidad de complejidad en cualquier paso del proceso.

Sin embargo, lo más importante es que los grupos de comandos son en sí mismos comandos- implementan la interfaz de Comando. Esto permite que los grupos de comandos se compongan de forma recursiva <https://en.wikipedia.org/wiki/Object_composition#Recursive_composition> __ - es decir, un grupo de comandos puede contener otros grupos de comandos como componentes.

Tipos de grupos de comandos

Nota

En la biblioteca basada en comandos de C ++, los grupos de comandos poseen sus comandos de componentes. Esto significa que los comandos pasados a los grupos de comandos se moverán o copiarán dependiendo de si son rvalues o lvalues (explicación rvalue/lvalue). Debido a ciertas preocupaciones técnicas, los grupos de comando en sí no se pueden copiar, por lo que la composición recursiva debe usar semántica de movimiento.

La biblioteca basada en comandos admite cuatro tipos básicos de grupos de comandos: SequentialCommandGroup, ParallelCommandGroup, ParallelRaceGroup, y ParallelDeadlineGroup. Cada uno de estos grupos de comandos combina varios comandos en un comando compuesto; sin embargo, lo hacen de diferentes maneras:

Grupo de comandos secuencial

Un SequentialCommandGroup (Java, C++) ejecuta una lista de comandos en secuencia - el primer comando será ejecutado, luego el segundo, luego el tercero, y así sucesivamente hasta que la lista termine. El grupo secuencial termina después de que el último comando de la secuencia termine. Por lo tanto, suele ser importante asegurarse de que cada comando de la secuencia termine realmente (si un comando determinado no termina, el siguiente nunca comenzará).

Grupo de comandos pararlelo

Un ParallelCommandGroup (Java, C++) ejecuta un conjunto de comandos de forma concurrente - todos los comandos se ejecutarán al mismo tiempo. El grupo paralelo finalizará cuando todos los comandos hayan terminado.

ParallelRaceGroup

Un ParallelRaceGroup (Java, C++) es muy parecido a un ParallelCommandgroup, en el sentido de que ejecuta un conjunto de comandos de forma concurrente. Sin embargo, el grupo de carrera termina tan pronto como cualquier comando en el grupo termina - todos los otros comandos se interrumpen en ese punto.

ParallelDeadlineGroup

Un ParallelDeadlineGroup (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ParallelDeadlineGroup.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1ParallelDeadlineGroup.html> __) también ejecuta un conjunto de comandos al mismo tiempo. Sin embargo, el grupo de fecha límite termina cuando un comando específico (la «fecha límite») finaliza, interrumpiendo todos los demás comandos en el grupo que todavía se están ejecutando en ese punto.

Crear grupos de comandos

Los usuarios tienen varias opciones para crear grupos de comandos. Una forma - similar a la implementación anterior de la biblioteca basada en comandos - es subclasificar una de las clases de grupos de comandos. Considere lo siguiente del proyecto de ejemplo de Hatch Bot (Java, C++):

 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package edu.wpi.first.wpilibj.examples.hatchbottraditional.commands;

import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;

import edu.wpi.first.wpilibj.examples.hatchbottraditional.Constants.AutoConstants;
import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.DriveSubsystem;
import edu.wpi.first.wpilibj.examples.hatchbottraditional.subsystems.HatchSubsystem;

/**
 * A complex auto command that drives forward, releases a hatch, and then drives backward.
 */
public class ComplexAuto extends SequentialCommandGroup {
  /**
   * Creates a new ComplexAuto.
   *
   * @param drive The drive subsystem this command will run on
   * @param hatch The hatch subsystem this command will run on
   */
  public ComplexAuto(DriveSubsystem drive, HatchSubsystem hatch) {
    addCommands(
        // Drive forward the specified distance
        new DriveDistance(AutoConstants.kAutoDriveDistanceInches, AutoConstants.kAutoDriveSpeed,
                          drive),

        // Release the hatch
        new ReleaseHatch(hatch),

        // Drive backward the specified distance
        new DriveDistance(AutoConstants.kAutoBackupDistanceInches, -AutoConstants.kAutoDriveSpeed,
                          drive));
  }

}

El método addCommands() agrega comandos al grupo y está presente en los cuatro tipos de grupos de comandos.

Grupos de comandos en línea

Nota

Debido a la verbosidad de la nueva sintaxis de Java, el objeto Java CommandGroupBase ofrece un método de fábrica para cada uno de los cuatro tipos de grupos de comandos: sequence, parallel, race, and deadline.

Los grupos de comandos se pueden usar sin subclasificar en absoluto: uno puede simplemente pasar los comandos deseados a través del constructor:

new SequentialCommandGroup(new FooCommand(), new BarCommand());

Esto se denomina una definición de comando: ref: inline <docs/software/commandbased/convenience-features:Inline Command Definitions>, y es muy útil para circunstancias en las que no es probable que los grupos de comandos se reutilicen, y escribir una clase completa para ellos sería un desperdicio.

Composición recursiva de grupos de comandos

Como se mencionó anteriormente, los grupos de comandos son componibles recursivamente <https://en.wikipedia.org/wiki/Object_composition#Recursive_composition> __ - dado que los grupos de comandos son comandos en sí mismos, pueden incluirse como componentes de otros grupos de comandos. Esta es una característica extremadamente poderosa de los grupos de comandos y permite a los usuarios construir acciones de robot muy complejas a partir de piezas simples. Por ejemplo, considere el siguiente código:

new SequentialCommandGroup(
   new DriveToGoal(m_drive),
   new ParallelCommandGroup(
      new RaiseElevator(m_elevator),
      new SetWristPosition(m_wrist)),
   new ScoreTube(m_wrist));

Esto crea un grupo de comando secuencial que contiene un grupo de comando paralelo. El flujo de control resultante se parece a esto:

command group with concurrency

Observe cómo la composición recursiva permite la incrustación de una estructura de control paralela dentro de una secuencial. Observe también que toda esta estructura, más compleja, podría estar nuevamente incrustada en otra estructura. La composición es una herramienta extremadamente poderosa, y los usuarios deben asegurarse de usar ampliamente.

Grupos de comandos y requisitos

Como los grupos de comandos son comandos, también deben declarar sus requisitos. Sin embargo, los usuarios no están obligados a especificar los requisitos manualmente para los grupos de comandos; los requisitos se infieren automáticamente de los comandos incluidos. Como regla, los grupos de comandos incluyen la unión de todos los subsistemas requeridos por los comandos de sus componentes. Por lo tanto, el ComplexAuto mostrado anteriormente requerirá tanto el subsistema de drive como el subsistema hatch del robot.

Además, los requisitos se aplican dentro de los tres tipos de grupos paralelos: un grupo paralelo puede no contener varios comandos que requieren el mismo subsistema.

Algunos usuarios avanzados pueden encontrar esto demasiado restrictivo; para dichos usuarios, la biblioteca ofrece la clase ScheduleCommand (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1ScheduleCommand.html> __) que se puede utilizar para «ramificarse» independientemente de los grupos de comandos para proporcionar una granularidad más fina en la gestión de requisitos.

Restricciones en componentes del grupo de comandos

Nota

Lo siguiente solo es relevante para la biblioteca basada en comandos de Java; El modelo de propiedad de la biblioteca C ++ naturalmente evita que los usuarios cometan esta categoría de error.

Dado que los componentes del grupo de comando se ejecutan a través de sus grupos de comando encapsulados, podrían producirse errores si esas mismas instancias de comando se programaran de forma independiente al mismo tiempo que el grupo; el comando se ejecutaría desde varios lugares a la vez y, por lo tanto, podría terminar siendo inconsistente estado interno, lo que provoca un comportamiento inesperado y difícil de diagnosticar.

Por esta razón, las instancias de comando que se han agregado a un grupo de comando no se pueden programar ni agregar de forma independiente a un segundo grupo de comando. Si lo intenta, se producirá una excepción y se bloqueará el programa de usuario.

Los usuarios avanzados que deseen reutilizar una instancia de comando y estén seguros de que es seguro hacerlo pueden saltarse esta restricción con el borrarComandoAgrupado() método <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.html#clearGroupedCommand(edu.wpi.first.wpilibj2.command.Command)>`__ en la clase BasedelGrupodeMando.