Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions source/docs/software/commandbased/command-compositions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,74 @@ The ``unless()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/rele
button.onTrue(command.unless(lambda: not intake.isDeployed()))
```

The ``onlyIf()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyIf(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#a73b3bb7908543e6ded5901adb0667c6d), :external:py:meth:`Python <commands2.Command.onlyIf>`) is similar to ``unless()`` but with inverted logic - the command will only run if the condition is true.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ``onlyIf()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyIf(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#a73b3bb7908543e6ded5901adb0667c6d), :external:py:meth:`Python <commands2.Command.onlyIf>`) is similar to ``unless()`` but with inverted logic - the command will only run if the condition is true.
The ``onlyIf()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyIf(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#a0c1b9e9e7cedd134145ee3d808bd92d2), :external:py:meth:`Python <commands2.Command.onlyIf>`) is similar to ``unless()`` but with inverted logic - the command will only run if the condition is true.


.. tab-set-code::

```java
// Command will only run if the game piece is present
button.onTrue(command.onlyIf(() -> intake.hasGamePiece()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
button.onTrue(command.onlyIf(() -> intake.hasGamePiece()));
button.onTrue(command.onlyIf(intake::hasGamePiece));

```

```c++
// Command will only run if the game piece is present
button.OnTrue(command.OnlyIf([&intake] { return intake.HasGamePiece(); }));
```

```python
# Command will only run if the game piece is present
button.onTrue(command.onlyIf(lambda: intake.hasGamePiece()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
button.onTrue(command.onlyIf(lambda: intake.hasGamePiece()))
button.onTrue(command.onlyIf(intake.hasGamePiece))

@virtuald Is this suggested style for robotpy users?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there's several more locations that need similar updates

```

The ``onlyWhile()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyWhile(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#a0ef33b5e59bf0f0b42010c8d6bec4d00), :external:py:meth:`Python <commands2.Command.onlyWhile>`) composes a command with a condition that is continuously checked during execution. If the condition becomes false while the command is running, the command will be interrupted. This is backed by a ``ParallelRaceGroup`` with a ``WaitUntilCommand``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ``onlyWhile()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyWhile(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#a0ef33b5e59bf0f0b42010c8d6bec4d00), :external:py:meth:`Python <commands2.Command.onlyWhile>`) composes a command with a condition that is continuously checked during execution. If the condition becomes false while the command is running, the command will be interrupted. This is backed by a ``ParallelRaceGroup`` with a ``WaitUntilCommand``.
The ``onlyWhile()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Command.html#onlyWhile(java.util.function.BooleanSupplier)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_command_ptr.html#ab4917ac5eb58bd08eb2412070d29ba1c), :external:py:meth:`Python <commands2.Command.onlyWhile>`) composes a command with a condition that is continuously checked during execution. If the condition becomes false while the command is running, the command will be interrupted. This is backed by a ``ParallelRaceGroup`` with a ``WaitUntilCommand``.


.. tab-set-code::

```java
// Command will run only while the mechanism is below the target position, stopping if it reaches or exceeds the target
button.onTrue(command.onlyWhile(() -> mechanism.getPosition() < targetPosition));
```

```c++
// Command will run only while the mechanism is below the target position, stopping if it reaches or exceeds the target
button.OnTrue(command.OnlyWhile([&mechanism, targetPosition] { return mechanism.GetPosition() < targetPosition; }));
```

```python
# Command will run only while the mechanism is below the target position, stopping if it reaches or exceeds the target
button.onTrue(command.onlyWhile(lambda: mechanism.getPosition() < targetPosition))
```

``ProxyCommand`` described below also has a constructor overload ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/ProxyCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_proxy_command.html), :external:py:class:`Python <commands2.ProxyCommand>`) that calls a command-returning lambda at schedule-time and runs the returned command by proxy.

The ``Defer`` factory ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Commands.html#defer(java.util.function.Supplier,java.util.Set)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/namespacefrc2_1_1cmd.html#a03fe6ddac82ad3a4e1c086b1c0c23422), :external:py:func:`Python <commands2.cmd.defer>`), backed by the ``DeferredCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/DeferredCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_deferred_command.html), :external:py:class:`Python <commands2.DeferredCommand>`), defers command construction to schedule-time by calling a lambda that returns a command. Unlike ``ProxyCommand``, the deferred command runs inline within the composition, keeping everything within the composition instead of delegating to the scheduler. This should generally be preferred over the deferred ``ProxyCommand`` constructor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ``Defer`` factory ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Commands.html#defer(java.util.function.Supplier,java.util.Set)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/namespacefrc2_1_1cmd.html#a03fe6ddac82ad3a4e1c086b1c0c23422), :external:py:func:`Python <commands2.cmd.defer>`), backed by the ``DeferredCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/DeferredCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_deferred_command.html), :external:py:class:`Python <commands2.DeferredCommand>`), defers command construction to schedule-time by calling a lambda that returns a command. Unlike ``ProxyCommand``, the deferred command runs inline within the composition, keeping everything within the composition instead of delegating to the scheduler. This should generally be preferred over the deferred ``ProxyCommand`` constructor.
The ``Defer`` factory ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/Commands.html#defer(java.util.function.Supplier,java.util.Set)), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/namespacefrc2_1_1cmd.html#ac177b5a1115cf55d575d5295c25ab7d1), :external:py:func:`Python <commands2.cmd.defer>`), backed by the ``DeferredCommand`` class ([Java](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj2/command/DeferredCommand.html), [C++](https://github.wpilib.org/allwpilib/docs/release/cpp/classfrc2_1_1_deferred_command.html), :external:py:class:`Python <commands2.DeferredCommand>`), defers command construction to schedule-time by calling a lambda that returns a command. Unlike ``ProxyCommand``, the deferred command runs inline within the composition, keeping everything within the composition instead of delegating to the scheduler. This should generally be preferred over the deferred ``ProxyCommand`` constructor.


.. tab-set-code::

```java
// Selects one of two commands based on current sensor state when scheduled
Commands.defer(
() -> sensor.get() ? Commands.print("Sensor true") : Commands.print("Sensor false"),
Set.of()
)
```

```c++
// Selects one of two commands based on current sensor state when scheduled
frc2::cmd::Defer(
[&sensor] { return sensor.Get() ? frc2::cmd::Print("Sensor true") : frc2::cmd::Print("Sensor false"); },
{}
)
```

```python
# Selects one of two commands based on current sensor state when scheduled
commands2.cmd.defer(
lambda: commands2.cmd.print_("Sensor true") if sensor.get() else commands2.cmd.print_("Sensor false"),
[]
)
```
Comment on lines +270 to +292
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I think you'd use ConditionalCommand, which also evaluates the predicate at runtime.


### Scheduling Other Commands

By default, composition members are run through the command composition, and are never themselves seen by the scheduler. Accordingly, their requirements are added to the composition's requirements. While this is usually fine, sometimes it is undesirable for the entire command composition to gain the requirements of a single command. A good solution is to "fork off" from the command composition and schedule that command separately. However, this requires synchronization between the composition and the individually-scheduled command.
Expand Down
Loading