便捷功能

虽然先前描述的方法对于编写基于命令的机器人代码将很好地工作,但是基于命令的库包含一些高级功能,可帮助高级用户大大减少基于命令的代码的冗长/复杂性。强烈建议用户熟悉这些功能,以最大化从基于命令的库中获得的价值。

内联命令定义

尽管用户可以通过显式编写命令类(通过子类化“CommandBase”或实现“Command”)来创建命令,但是对于许多命令(例如那些仅调用单个子系统方法的命令)而言,这涉及很多浪费的样板代码。为了缓解这种情况,可以*内嵌*基于命令的库中包含的许多预写命令-即,可以在命令构造时以单行代码定义命令主体。

将子例程作为参数传递

为了内联命令定义,用户需要某种方式来指定命令将作为构造函数参数运行的代码。幸运的是,Java和C ++都为用户提供了将子例程作为参数传递的功能。

方法参考(Java)

在Java中,对可以作为参数传递的子例程的引用称为方法引用。方法参考的一般语法是“object::method”。请注意,由于方法*itself*是参数,因此不包括方法参数。该方法没有被调用-它被传递给另一段代码(在本例中为命令),以便*那个*代码可以在需要时调用它。有关方法引用的更多信息,请参见`the official Oracle documentation <https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html>`__。

Lambda表达式(Java)

尽管方法引用可以很好地传递已编写的子例程,但是如果子例程永远不会在其他地方使用,则仅出于发送方法引用的目的而编写子例程通常是不便/浪费的。为避免这种情况,Java还支持称为“ lambda表达式”的功能。 Lambda表达式是一个内联方法定义-它允许在*参数列表*内定义子例程。有关如何编写Java lambda表达式的详细信息,请参见本教程<https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#syntax>`__.。

Lambda表达式(C ++)

警告

由于C ++语义的复杂性,如果从命令组的组件命令执行此操作,则在C ++ lambda中捕获``this’’会导致空指针异常。 C ++用户应尽可能明确并按值捕获相关的命令成员。有关更多详细信息,请参见`here <https://github.com/wpilibsuite/allwpilib/issues/3109>`__。

C ++缺少与Java方法引用几乎相同的功能-成员函数的指针由于存在隐式的“this”参数而通常不能直接用作参数。但是,C ++确实提供了lambda表达式——此外,C ++提供的lambda表达式在许多方面都比Java语言更强大。有关如何编写C ++ lambda表达式的详细信息,请参见`cppreference <https://en.cppreference.com/w/cpp/language/lambda>`__。

内联命令示例

那么,内联命令定义实际上是什么样的?

InstantCommand类提供了一个受益于内联的命令类型的示例。考虑一下HatchBotInlined示例项目中的以下内容(Java <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/hatchbotinlined> __,C ++ <https://github.com/wpilibsuite/allwpilib/tree/main/wpilibcExamples/src/main/cpp/examples/HatchbotInlined> __):

 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));

不必浪费时间编写单独的“GrabHatch”和“ReleaseHatch”命令,这些命令在结束前仅调用一个方法,而可以通过传递适当的子系统方法,通过简单的内联定义来完成这两个命令。

包含的命令类型

基于命令的库包含各种针对常见用例的预先编写的命令。这些命令中的许多命令都旨在通过内联<#inline-command-definitions> _来“开箱即用”使用,但是它们也可以被子类化。您可以在下面找到包含的预制命令列表,以及每个命令的简短示例-有关更严格的文档,请参阅API文档(Java api <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类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ConditionalCommand.html>`__,C ++)在执行时会运行两个命令之一,具体取决于用户指定的是非条件:

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

SelectCommand

注解

Java版本的SelectCommand仅使用“Object”作为键,而C ++版本则以键类型为模板。

注解

SelectCommand的替代版本仅采用一种提供要运行的命令的方法——这可能非常简洁,但是使推断命令的需求成为不可能,因此,用户需要手动将需求添加到SelectCommand。

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>`__)是ConditionalCommand类的概括,它根据用户指定的选择器的值运行选择的命令之一。以下示例代码摘自SelectCommand示例项目(JavaC ++):

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

InstantCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/InstantCommand.html>`__,C ++)在初始化时执行一个动作,然后立即结束:

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

RunCommand

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>`__)在其execute()块中重复运行指定的方法。默认情况下,它没有结束条件。用户可以将其子类化,也可以`装饰<#command-decorator-methods>`_以添加它们。

// 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

StartEndCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/StartEndCommand.html>`__,C ++)在开始时执行一个动作,在结束时执行第二个动作。默认情况下,它没有结束条件。用户可以将其子类化,也可以`装饰<#command-decorator-methods>`_内联命令来添加它们。

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

FunctionCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/FunctionalCommand.html>`__,C ++)允许将所有四个Command方法作为方法引用或lambda传入:

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

PrintCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/PrintCommand.html>`__,C ++)打印给定的字符串。

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

ScheduleCommand

ScheduleCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ScheduleCommand.html>`__,C ++)调度指定的命令并立即结束:

// Schedules commandToSchedule when run
new ScheduleCommand(commandToSchedule)

这通常对于从命令组“forking off”很有用:默认情况下,命令组中的命令是*通过*命令组运行的,而调度程序永远不会看到它们。因此,他们的要求被添加到组的要求中。尽管这通常很好,但有时整个命令组都不希望获得单个命令的要求——一个好的解决方案是从命令组“fork off”并单独计划该命令。

ProxyScheduleCommand

ProxyScheduleCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ProxyScheduleCommand.html>`__,C ++)计划指定的命令,并且直到该命令结束才结束:

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

这通常对于从命令组“forking off”很有用:默认情况下,命令组中的命令是通过命令组运行的,而调度程序永远不会看到它们。因此,他们的要求被添加到组的要求中。虽然这通常很好,但是有时整个命令组都不希望获得单个命令的要求——一个好的解决方案是从命令组“fork iff”并单独计划命令。

WaitCommand

``WaitCommand``类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/WaitCommand.html>`__,C ++)不执行任何操作,并在其初始调度后经过指定的时间段后结束:

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

这通常作为命令组的组成部分很有用。

也可以将``WaitCommand``子类化以创建运行一段时间的更复杂的命令。如果在此方法中使用了“ WaitCommand”,则用户必须确保仍然调用“ WaitCommand”的“ Initialize”,“ End”和“ IsFinished”方法,以便WaitCommand的计时器起作用。

WaitUnitilCommand

警告

WaitUntilCommand使用的比赛计时器*不*提供官方比赛时间!虽然非常准确,但是使用此计时器*不能*保证机器人动作的合法性。

``WaitUntilCommand``类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/WaitUntilCommand.html>`__,C ++)不执行任何操作,并在指定条件为真或经过指定的匹配时间后结束。

// 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

PerpetualCommand类(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/PerpetualCommand.html>`__,C ++)运行给定命令并删除其终止条件,以便其永久运行(除非外部中断):

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

命令装饰器的方法

“ Command”界面包含许多默认的“ decorator”方法,可用于向现有命令添加其他功能。 “装饰器”方法是一种方法,该方法接受一个对象(在这种情况下为命令)并返回相同类型的对象(即命令),并添加了一些其他功能。下面包括了包含的装饰器方法的列表以及简短的示例-有关严格的文档,请参见API文档(Java Java 0533721bad8e2z0 __,C ++ <https://first.wpi.edu/wpilib/allwpilib/docs/release/cpp/classfrc2_1_1Command.html> __)。

withTimeout

withTimeout()装饰器(JavaC ++)将超时添加到命令中。如果超时到期,修饰的命令将被中断:

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

withInterrupt

withInterrupt()``(Java <https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#withInterrupt(java.util.function.BooleanSupplier)>`__,C ++)装饰器添加了一个条件,命令将被中断:

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

andThen

andThen()装饰器(JavaC ++)添加了一个在命令结束后要执行的方法:

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

beforeStarting

``beforeStarting()``装饰器(JavaC ++)添加了要在命令启动之前执行的方法:

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

alongWith(仅Java)

注解

由于技术限制,C ++不支持此装饰器——用户应简单地以常规方式构造并行命令组。

The alongWith() decorator returns a parallel command group. 所有指令会在同时执行,并各自独立结束:

// 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(仅Java)

注解

由于技术限制,C ++不支持该装饰器——用户应该简单地以常规方式构造一个并行竞争组。

The raceWith() decorator returns a parallel race group 此时,所有其他的都被中断。哪个命令是调用命令并不重要:

// 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(仅Java)

注解

由于技术限制,C ++不支持此装饰器——用户应简单地以常规方式构造并行的截止时间组。

The deadlineWith() decorator returns a parallel deadline group 调用命令为截止日期。当该截止日期命令结束时,它将中断所有其他未完成的命令:

// 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(仅Java)

注解

由于技术限制,C ++不支持此装饰器——用户应在其命令类中设置命令名称。

withName()装饰器<https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandBase.html#withName(java.lang.String)>`__为命令添加名称。通过可发送接口<https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandBase.html#initSendable(edu.wpi.first.wpilibj.smartdashboard.SendableBuilder)>`__发送命令时,此名称将出现在仪表板上。

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

perpetually

``perpetually()’’装饰器(JavaC ++)删除了命令的结束条件,因此它可以永远运行。

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

组成装饰器

请记住,装饰器和所有命令组一样都可以组成!这允许非常强大而简洁的内联表达式:

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

命令组的静态工厂方法 (仅Java)

注解

这些工厂方法未包含在C ++命令库中,因为冗长程度的降低极小——C ++命令应进行堆栈分配,而无需使用“new”关键字。

如果用户不希望使用装饰器“ andThen”,“ alongWith”,“ raceWith”和“ deadlineWith”修饰符来声明命令组,但与调用构造函数相比,仍然希望减少冗长性, CommandGroupBase''类<https://first.wpi.edu/wpilib/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/CommandGroupBase.html>`__包含四个用于声明命令组的静态工厂方法:``sequence()parallel()``race()``和``deadline( )。当从命令组子类中使用或与``import static’’结合使用时,这些变得非常简洁,并极大地有助于命令的编写

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()
        )
    );
  }

}