Funciones de conveniencia

Si bien las metodologías descritas anteriormente funcionarán bien para escribir código de robot basado en comandos, las bibliotecas basadas en comandos contienen varias características de conveniencia para usuarios más avanzados que pueden reducir en gran medida la verbosidad / complejidad del código basado en comandos. Se recomienda encarecidamente que los usuarios se familiaricen con estas funciones para maximizar el valor que obtienen de las bibliotecas basadas en comandos.

Definiciones de comandos en línea

Si bien los usuarios pueden crear comandos escribiendo explícitamente clases de comandos (ya sea subclasificando CommandBase o implementando``Command``), para muchos comandos (como aquellos que simplemente llaman a un método de subsistema único) esto implica una gran cantidad de código repetitivo derrochador. Para ayudar a aliviar esto, muchos de los comandos preescritos incluidos en la biblioteca basada en comandos pueden estar alineados, es decir, el cuerpo del comando se puede definir en una sola línea de código en la construcción del comando.

Pasar subrutinas como parámetros

Para insertar una definición de comando, los usuarios necesitan alguna forma de especificar qué código ejecutarán los comandos como parámetros del constructor. Afortunadamente, tanto Java como C++ ofrecen a los usuarios la posibilidad de pasar subrutinas como parámetros.

Referencias de métodos (Java)

En Java, una referencia a una subrutina que se puede pasar como parámetro se denomina referencia de método. La sintaxis general para una referencia a un método es object::method. Tenga en cuenta que no se incluyen parámetros de método, ya que el método en sí es el parámetro. No se está llamando al método, se está pasando a otro fragmento de código (en este caso, un comando) para que ese código pueda llamarlo cuando sea necesario. Para obtener más información sobre referencias de métodos, consulte la documentación oficial de Oracle.

Expresiones Lambda (Java)

Si bien las referencias a métodos funcionan bien para pasar una subrutina que ya se ha escrito, a menudo es inconveniente / inútil escribir una subrutina únicamente con el propósito de enviarla como referencia de método, si esa subrutina nunca se usará en otro lugar. Para evitar esto, Java también admite una función llamada «expresiones lambda». Una expresión lambda es una definición de método en línea: permite definir una subrutina dentro de una lista de parámetros. Para obtener detalles sobre cómo escribir expresiones lambda de Java, consulte este tutorial.

Expresiones Lambda (C++)

Advertencia

Due to complications in C++ semantics, capturing this in a C++ lambda can cause a null pointer exception if done from a component command of a command group. Whenever possible, C++ users should capture relevant command members explicitly and by value. For more details, see here.

C++ carece de un equivalente cercano a las referencias a métodos de Java: los punteros a funciones miembro generalmente no se pueden usar directamente como parámetros debido a la presencia del parámetro implícito``this``. Sin embargo, C++ ofrece expresiones lambda; además, las expresiones lambda que ofrece C++ son, en muchos sentidos, más poderosas que las de Java. Para obtener detalles sobre cómo escribir expresiones lambda de C++, consulte cppreference.

Ejemplo de comando en línea

Entonces, ¿cómo se ve una definición de comando en línea en la práctica?

La clase InstantCommand proporciona un ejemplo de un tipo de comando que se beneficia enormemente de la inserción. Considere lo siguiente del proyecto de ejemplo HatchBotInlined (Java, C++):

 99
100
101
102
103
104
    new JoystickButton(m_driverController, Button.kB.value)
        .whenPressed(new InstantCommand(m_hatchSubsystem::releaseHatch, m_hatchSubsystem));
    // While holding the shoulder button, drive at half speed
    new JoystickButton(m_driverController, Button.kBumperRight.value)
        .whenPressed(() -> m_robotDrive.setMaxOutput(0.5))
        .whenReleased(() -> m_robotDrive.setMaxOutput(1));

En lugar de escribir inútilmente comandos separados GrabHatch y ReleaseHatch que llaman solo a un método antes de finalizar, ambos pueden lograrse con una definición en línea simple pasando el método de subsistema apropiado.

Tipos de comandos incluidos

La biblioteca basada en comandos incluye una variedad de comandos preescritos para casos de uso comunes. Muchos de estos comandos están pensados para ser usados «fuera de la caja» a través de inlining <#inline-command-definitions> _, sin embargo, también pueden ser subclasificados. A continuación se puede encontrar una lista de los comandos prefabricados incluidos, junto con breves ejemplos de cada uno; para obtener una documentación más rigurosa, consulte los documentos de la API (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/package-summary.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1Command.html> __).

ConditionalCommand

La clase ConditionalCommand (Java, C++) corre uno de los dos comandos cuando se ejecuta, dependiendo de una condición verdadera o falsa especificada por el usuario:

// Runs either commandOnTrue or commandOnFalse depending on the value of m_limitSwitch.get()
new ConditionalCommand(commandOnTrue, commandOnFalse, m_limitSwitch::get)

SelectCommand

Nota

Mientras que la versión Java de SelectCommand simplemente usa un Object como clave, la versión C++ se basa en el tipo de clave.

Nota

Una versión alternativa de SelectCommand simplemente toma un método que proporciona el comando que se ejecutará; esto puede ser muy conciso, pero hace imposible inferir los requisitos del comando y, por lo tanto, deja al usuario responsable de agregar manualmente los requisitos al SelectCommand.

La clase SelectCommand (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/SelectCommand.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1SelectCommand.html> __) es una generalización de la clase ``ConditionalCommand `` que ejecuta uno de una selección de comandos basados en el valor de un selector especificado por el usuario. El siguiente código de ejemplo se toma del proyecto de ejemplo SelectCommand (Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/selectcommand> __, C ++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/SelectCommand> __):

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class RobotContainer {
  // The enum used as keys for selecting the command to run.
  private enum CommandSelector {
    ONE,
    TWO,
    THREE
  }

  // An example selector method for the selectcommand.  Returns the selector that will select
  // which command to run.  Can base this choice on logical conditions evaluated at runtime.
  private CommandSelector select() {
    return CommandSelector.ONE;
  }

  // An example selectcommand.  Will select from the three commands based on the value returned
  // by the selector method at runtime.  Note that selectcommand works on Object(), so the
  // selector does not have to be an enum; it could be any desired type (string, integer,
  // boolean, double...)
  private final Command m_exampleSelectCommand =
      new SelectCommand(
          // Maps selector values to commands
          Map.ofEntries(
              Map.entry(CommandSelector.ONE, new PrintCommand("Command one was selected!")),
              Map.entry(CommandSelector.TWO, new PrintCommand("Command two was selected!")),
              Map.entry(CommandSelector.THREE, new PrintCommand("Command three was selected!"))),
          this::select);

InstantCommand

La clase InstantCommand (Java, C++) ejecuta una sola acción en la inicialización, y luego termina inmediatamente:

// Actuates the hatch subsystem to grab the hatch
new InstantCommand(m_hatchSubsystem::grabHatch, m_hatchSubsystem)

RunCommand

La clase RunCommand `` (`Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/RunCommand.html>` __, `C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1RunCommand.html>` __) ejecuta un método especificado repetidamente en su bloque ``execute(). No tiene condiciones finales por defecto; los usuarios pueden subclasificarlo o `decorarlo <#command-decorator-methods> para agregarlo.

// A split-stick arcade command, with forward/backward controlled by the left
// hand, and turning controlled by the right.
new RunCommand(() -> m_robotDrive.arcadeDrive(
    -driverController.getY(GenericHID.Hand.kLeft),
    driverController.getX(GenericHID.Hand.kRight)),
    m_robotDrive)

StartEndCommand

La clase StartEndCommand (Java, C++) ejecuta una acción al iniciar y una segunda al finalizar. No tiene condiciones finales por defecto; los usuarios pueden subclasificarlo o decorate un comando en línea para agregarlos.

new StartEndCommand(
    // Start a flywheel spinning at 50% power
    () -> m_shooter.shooterSpeed(0.5),
    // Stop the flywheel at the end of the command
    () -> m_shooter.shooterSpeed(0.0),
    // Requires the shooter subsystem
    m_shooter
)

FunctionalCommand

La clase FunctionalCommand (Java, C++) permite que los cuatro métodos de Command se pasen como referencias de método o lambdas:

new FunctionalCommand(
    // Reset encoders on command start
    m_robotDrive::resetEncoders,
    // Start driving forward at the start of the command
    () -> m_robotDrive.arcadeDrive(kAutoDriveSpeed, 0),
    // Stop driving at the end of the command
    interrupted -> m_robotDrive.arcadeDrive(0, 0),
    // End the command when the robot's driven distance exceeds the desired value
    () -> m_robotDrive.getAverageEncoderDistance() >= kAutoDriveDistanceInches,
    // Require the drive subsystem
    m_robotDrive
)

PrintCommand

La clase PrintCommand (Java, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1PrintCommand.html> __) imprime un string determinado.

new PrintCommand("This message will be printed!")

ScheduleCommand

La clase ScheduleCommand (Java, C++) programa un comando en específico, que termina instantáneamente.

// Schedules commandToSchedule when run
new ScheduleCommand(commandToSchedule)

Esto suele ser útil para «forking off» de los grupos de comandos: de forma predeterminada, los comandos de los grupos de comandos se ejecutan * a través * del grupo de comandos y el planificador nunca los ve. En consecuencia, sus requisitos se suman a los del grupo. Si bien esto suele estar bien, a veces no es deseable que todo el grupo de comandos obtenga los requisitos de un solo comando; una buena solución es «fork off» del grupo de comandos y programar ese comando por separado.

ProxyScheduleCommand

La clase ProxyScheduleCommand (Java, C++) programa un comando en especifico, y no termina hasta que ese comando acabe.

// Schedules commandToSchedule when run, does not end until commandToSchedule is no longer scheduled
new ProxyScheduleCommand(commandToSchedule)

Esto suele ser útil para «forking off» de los grupos de comandos: de forma predeterminada, los comandos de los grupos de comandos se ejecutan mediante del grupo de comandos y el planificador nunca los ve. En consecuencia, sus requisitos se suman a los del grupo. Si bien esto suele estar bien, a veces no es deseable que todo el grupo de comandos obtenga los requisitos de un solo comando; una buena solución es «fork off» del grupo de comandos y programar el comando por separado.

WaitCommand

La clase WaitCommand (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/WaitCommand.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1WaitCommand.html> __) no hace nada y finaliza después de un período de tiempo especificado después de su programación inicial:

// Ends 5 seconds after being scheduled
new WaitCommand(5)

Suele resultar útil como componente de un grupo de mando.

WaitCommand puede ser también se puede subclasificar para crear un comando más complicado que se ejecute por un periodo de tiempo. Si WaitCommand es usado en este método, el usuario debe asegurarse que el WaitCommand`”s Initialize, End y IsFinished siguen siendo llamados para que el temporizador del WaitCommand funcione.

WaitUntilCommand

Advertencia

¡El temporizador de partido utilizado por WaitUntilCommand no proporciona una hora oficial de partido! Si bien es bastante preciso, el uso de este temporizador no puede garantizar la legalidad de las acciones de su robot.

La clase WaitUntilCommand`` (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.html> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1WaitUntilCommand.html> __) no hace nada y finaliza una vez que una condición especificada se convierte en verdadera, o hasta que pasa un tiempo de coincidencia especificado.

// Ends after the 60-second mark of the current match
new WaitUntilCommand(60)

// Ends after m_limitSwitch.get() returns true
new WaitUntilCommand(m_limitSwitch::get)

PerpetualCommand

La clase PerpetualCommand (Java, C++) ejecuta un comando dado con su condición final eliminada, de modo que se ejecuta para siempre (a menos que sea interrumpido externamente):

// Will run commandToRunForever perpetually, even if its isFinished() method returns true
new PerpetualCommand(commandToRunForever)

Métodos del decorador de comandos

La interfaz de Command contiene una serie de métodos de «decorador» predeterminados que se pueden usar para agregar funcionalidad adicional a los comandos existentes. Un método «decorador» es un método que toma un objeto (en este caso, un comando) y devuelve un objeto del mismo tipo (es decir, un comando) con alguna funcionalidad adicional agregada. A continuación se incluye una lista de los métodos decoradores incluidos con breves ejemplos; para obtener una documentación rigurosa, consulte los documentos de la API (Java, C++).

withTimeout

El decorador withTimeout() (Java, C++) añade un tiempo de espera a un comando. El comando decorado se interrumpirá si el tiempo de espera expira:

// Will time out 5 seconds after being scheduled, and be interrupted
button.whenPressed(command.withTimeout(5));

withInterrupt

El decorador ``withInterrupt () `` (Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#withInterrupt(java.util.function.BooleanSupplier)> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1Command.html#a4a6078c253a1578b3310833dcdfc01f0> __) agrega una condición en la que el comando será interrumpido:

// Will be interrupted if m_limitSwitch.get() returns true
button.whenPressed(command.withInterrupt(m_limitSwitch::get));

andThen

El decorador andThen() (Java, C++) añade un método para ser ejecutado después de que el comando termine:

// Will print "hello" after ending
button.whenPressed(command.andThen(() -> System.out.println("hello")));

beforeStarting

El decorador beforeStarting() (Java, C++) agrega un método para ser ejecutado antes que el comando inicie.

// Will print "hello" before starting
button.whenPressed(command.beforeStarting(() -> System.out.println("hello")));

alongWith (solo Java)

Nota

Este decorador no es compatible con C++ debido a limitaciones técnicas; los usuarios simplemente deberían construir un grupo de comandos paralelo de la forma habitual.

The alongWith() decorator returns a parallel command group. All commands will execute at the same time and each will end independently of each other:

// Will be a parallel command group that ends after three seconds with all three commands running their full duration.
button.whenPressed(oneSecCommand.alongWith(twoSecCommand, threeSecCommand));

raceWith (solo Java)

Nota

Este decorador no es compatible con C++ debido a limitaciones técnicas; los usuarios simplemente deberían construir un grupo de carreras paralelas de la forma habitual.

The raceWith() decorator returns a parallel race group that ends as soon as the first command ends. At this point all others are interrupted. It doesn’t matter which command is the calling command:

// Will be a parallel race group that ends after one second with the two and three second commands getting interrupted.
button.whenPressed(twoSecCommand.raceWith(oneSecCommand, threeSecCommand));

deadlineWith (solo Java)

Nota

Este decorador no es compatible con C++ debido a limitaciones técnicas; los usuarios simplemente deberían construir un grupo de fecha límite paralelo de la forma habitual.

The deadlineWith() decorator returns a parallel deadline group with the calling command being the deadline. When this deadline command ends it will interrupt any others that are not finished:

// Will be a parallel deadline group that ends after two seconds (the deadline) with the three second command getting interrupted (one second command already finished).
button.whenPressed(twoSecCommand.deadlineWith(oneSecCommand, threeSecCommand));

withName (solo Java)

Nota

Este decorador no es compatible con C++ debido a limitaciones técnicas; los usuarios deben establecer el nombre del comando dentro de su clase de comando.

El decorador withName() <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandBase.html#withName(java.lang.String)>`__ agrega un nombre a un comando. Este nombre aparecerá en la dashboard cuando el comando es enviado por medio de la interfaz de envío.

// This command will be called "My Command".
var command = new PrintCommand("Hello robot!").withName("My Command");

perpetuamente

El decorador ``perpetually()``(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#perpetually()> __, C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1Command.html#ae1583c73c9b953d8ff730d4809926518> __) elimina la condición de fin de un comando, de modo que se ejecute para siempre.

// Will run forever unless externally interrupted, regardless of command.isFinished()
button.whenPressed(command.perpetually());

Decoradores de composición

Recuerde que los decoradores, como todos los grupos de mando, se pueden componer. Esto permite expresiones en línea muy poderosas y concisas:

// Will run fooCommand, and then a race between barCommand and bazCommand
button.whenPressed(fooCommand.andThen(barCommand.raceWith(bazCommand)));

Métodos estáticos de fábrica para grupos de comandos (solo Java)

Nota

Estos métodos de fábrica no están incluidos en la biblioteca de comandos de C++, ya que la reducción de la verbosidad sería mínima; los comandos de C++ deben asignarse en pila, eliminando la necesidad de la palabra clave new.

Si los usuarios no desean usar los decoradores andThen, alongWith, raceWith, y deadlineWith para declarar grupos de comandos, pero aún desean reducir la verbosidad en comparación con llamar a los constructores, la clase CommandGroupBase <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.html> __ contiene cuatro métodos de fábrica estáticos para declarar grupos de comandos:``sequence()`, parallel(), race(), and deadline(). Cuando se usa desde una subclase de grupo de comando o en combinación con import static, estos se vuelven extremadamente concisos y ayudan mucho en la composición de comandos:

public class ExampleSequence extends SequentialCommandGroup {

  // Will run a FooCommand, and then a race between a BarCommand and a BazCommand
  public ExampleSequence() {
    addCommands(
        new FooCommand(),
        race(
            new BarCommand(),
            new BazCommand()
        )
    );
  }

}