From cae28ab5153a0711178e44b9613afdf1858c75bf Mon Sep 17 00:00:00 2001 From: jasondaming Date: Wed, 8 Oct 2025 16:38:22 -0500 Subject: [PATCH 1/3] Document DeferredCommand and onlyIf/onlyWhile decorators Adds comprehensive documentation for: - DeferredCommand and defer() factory for schedule-time command construction - onlyIf() decorator for conditional command execution - onlyWhile() decorator for commands that interrupt when condition becomes false Includes examples in Java, C++, and Python for all three decorators. Fixes #2368 Fixes #2252 --- .../commandbased/command-compositions.rst | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/source/docs/software/commandbased/command-compositions.rst b/source/docs/software/commandbased/command-compositions.rst index 4f17b8f745..b0ae659c79 100644 --- a/source/docs/software/commandbased/command-compositions.rst +++ b/source/docs/software/commandbased/command-compositions.rst @@ -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 `) 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())); + ``` + + ```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())) + ``` + +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 `) 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 `) 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 `), 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 `), 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"), + [] + ) + ``` + ### 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. From c730670007992d0d4c6ecb645d63acad3363ae65 Mon Sep 17 00:00:00 2001 From: jasondaming Date: Sun, 12 Oct 2025 14:01:23 -0500 Subject: [PATCH 2/3] Apply review feedback: fix API links and method references - Updated C++ onlyIf link to correct overload - Updated C++ onlyWhile link to correct overload - Updated C++ defer link to correct overload - Changed Java onlyIf example to use method reference (intake::hasGamePiece) - Changed Python onlyIf example to use method reference (intake.hasGamePiece) Co-authored-by: katzuv --- .../software/commandbased/command-compositions.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/docs/software/commandbased/command-compositions.rst b/source/docs/software/commandbased/command-compositions.rst index b0ae659c79..f47f5aac3f 100644 --- a/source/docs/software/commandbased/command-compositions.rst +++ b/source/docs/software/commandbased/command-compositions.rst @@ -223,13 +223,13 @@ 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 `) 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 `) 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())); + button.onTrue(command.onlyIf(intake::hasGamePiece)); ``` ```c++ @@ -239,10 +239,10 @@ The ``onlyIf()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/rele ```python # Command will only run if the game piece is present - button.onTrue(command.onlyIf(lambda: intake.hasGamePiece())) + button.onTrue(command.onlyIf(intake.hasGamePiece)) ``` -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 `) 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 `) 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:: @@ -263,7 +263,7 @@ The ``onlyWhile()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/r ``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 `) 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 `), 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 `), 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 `), 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 `), 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:: From 87cbc3d0cb23de01013807041b4433a8c077ccb5 Mon Sep 17 00:00:00 2001 From: jasondaming Date: Sun, 12 Oct 2025 15:43:53 -0500 Subject: [PATCH 3/3] Fix broken Python API reference for defer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove broken intersphinx link to commands2.cmd.defer which is not found in Python API documentation. Keep plain text 'Python' reference. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- source/docs/software/commandbased/command-compositions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/docs/software/commandbased/command-compositions.rst b/source/docs/software/commandbased/command-compositions.rst index f47f5aac3f..b406797382 100644 --- a/source/docs/software/commandbased/command-compositions.rst +++ b/source/docs/software/commandbased/command-compositions.rst @@ -263,7 +263,7 @@ The ``onlyWhile()`` decorator ([Java](https://github.wpilib.org/allwpilib/docs/r ``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 `) 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#ac177b5a1115cf55d575d5295c25ab7d1), :external:py:func:`Python `), 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 `), 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), Python), 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 `), 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::