Skip to content

Commit c5f2bb8

Browse files
committed
Validate various commands with minor adjustments
- Update docs - Change member variables to be prefixed with _ - Add initSendable
1 parent 6f1210d commit c5f2bb8

16 files changed

+216
-70
lines changed

Diff for: commands2/exceptions.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# notrack
12
class IllegalCommandUse(Exception):
23
"""
34
This exception is raised when a command is used in a way that it shouldn't be.

Diff for: commands2/functionalcommand.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS 6e58db398d63 FunctionalCommand.java
12
from __future__ import annotations
23

34
from typing import Any, Callable
@@ -11,7 +12,8 @@ class FunctionalCommand(Command):
1112
A command that allows the user to pass in functions for each of the basic command methods through
1213
the constructor. Useful for inline definitions of complex commands - note, however, that if a
1314
command is beyond a certain complexity it is usually better practice to write a proper class for
14-
it than to inline it."""
15+
it than to inline it.
16+
"""
1517

1618
def __init__(
1719
self,
@@ -28,7 +30,8 @@ def __init__(
2830
:param onExecute: the function to run on command execution
2931
:param onEnd: the function to run on command end
3032
:param isFinished: the function that determines whether the command has finished
31-
:param requirements: the subsystems required by this command"""
33+
:param requirements: the subsystems required by this command
34+
"""
3235
super().__init__()
3336

3437
assert callable(onInit)

Diff for: commands2/instantcommand.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS 5cf961edb973 InstantCommand.java
12
from __future__ import annotations
23

34
from typing import Callable, Optional
@@ -9,17 +10,19 @@
910
class InstantCommand(FunctionalCommand):
1011
"""
1112
A Command that runs instantly; it will initialize, execute once, and end on the same iteration of
12-
the scheduler. Users can either pass in a Runnable and a set of requirements, or else subclass
13-
this command if desired."""
13+
the scheduler. Users can either pass in a Callable and a set of requirements, or else subclass
14+
this command if desired.
15+
"""
1416

1517
def __init__(
1618
self, toRun: Optional[Callable[[], None]] = None, *requirements: Subsystem
1719
):
1820
"""
19-
Creates a new InstantCommand that runs the given Runnable with the given requirements.
21+
Creates a new InstantCommand that runs the given Callable with the given requirements.
2022
21-
:param toRun: the Runnable to run
22-
:param requirements: the subsystems required by this command"""
23+
:param toRun: the Callable to run
24+
:param requirements: the subsystems required by this command
25+
"""
2326
super().__init__(
2427
toRun or (lambda: None),
2528
lambda: None,

Diff for: commands2/notifiercommand.py

+17-13
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
1+
# validated: 2024-01-19 DS 6e58db398d63 NotifierCommand.java
12
from __future__ import annotations
23

34
from typing import Any, Callable
45

56
from wpilib import Notifier
7+
from wpimath import units
68

79
from .command import Command
810
from .subsystem import Subsystem
911

1012

1113
class NotifierCommand(Command):
1214
"""
13-
A command that starts a notifier to run the given runnable periodically in a separate thread. Has
14-
no end condition as-is; either subclass it or use Command#withTimeout(double) or {@link
15-
Command#until(java.util.function.BooleanSupplier)} to give it one.
15+
A command that starts a notifier to run the given Callable periodically in a separate thread. Has
16+
no end condition as-is; either subclass it or use :func:`commands2.Command.withTimeout` or :func:`commands2.Command.until` to give it one.
1617
17-
WARNING: Do not use this class unless you are confident in your ability to make the executed
18-
code thread-safe. If you do not know what "thread-safe" means, that is a good sign that you
19-
should not use this class."""
18+
.. warning:: Do not use this class unless you are confident in your ability
19+
to make the executed code thread-safe. If you do not know what
20+
"thread-safe" means, that is a good sign that you should not use
21+
this class.
22+
"""
2023

2124
def __init__(
22-
self, toRun: Callable[[], Any], period: float, *requirements: Subsystem
25+
self, toRun: Callable[[], Any], period: units.seconds, *requirements: Subsystem
2326
):
2427
"""
2528
Creates a new NotifierCommand.
2629
27-
:param toRun: the runnable for the notifier to run
30+
:param toRun: the Callable for the notifier to run
2831
:param period: the period at which the notifier should run, in seconds
29-
:param requirements: the subsystems required by this command"""
32+
:param requirements: the subsystems required by this command
33+
"""
3034
super().__init__()
3135

32-
self.notifier = Notifier(toRun)
33-
self.period = period
36+
self._notifier = Notifier(toRun)
37+
self._period = period
3438
self.addRequirements(*requirements)
3539

3640
def initialize(self):
37-
self.notifier.startPeriodic(self.period)
41+
self._notifier.startPeriodic(self._period)
3842

3943
def end(self, interrupted: bool):
40-
self.notifier.stop()
44+
self._notifier.stop()

Diff for: commands2/parallelcommandgroup.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS aaea85ff1656 ParallelCommandGroup.java
12
from __future__ import annotations
23

34
from typing import Dict
@@ -14,22 +15,29 @@ class ParallelCommandGroup(Command):
1415
1516
The rules for command compositions apply: command instances that are passed to it cannot be
1617
added to any other composition or scheduled individually, and the composition requires all
17-
subsystems its components require."""
18+
subsystems its components require.
19+
"""
1820

1921
def __init__(self, *commands: Command):
2022
"""
2123
Creates a new ParallelCommandGroup. The given commands will be executed simultaneously. The
2224
command composition will finish when the last command finishes. If the composition is
2325
interrupted, only the commands that are still running will be interrupted.
2426
25-
:param commands: the commands to include in this composition."""
27+
:param commands: the commands to include in this composition.
28+
"""
2629
super().__init__()
2730
self._commands: Dict[Command, bool] = {}
2831
self._runsWhenDisabled = True
2932
self._interruptBehavior = InterruptionBehavior.kCancelIncoming
3033
self.addCommands(*commands)
3134

3235
def addCommands(self, *commands: Command):
36+
"""
37+
Adds the given commands to the group.
38+
39+
:param commands: Commands to add to the group
40+
"""
3341
commands = flatten_args_commands(commands)
3442
if True in self._commands.values():
3543
raise IllegalCommandUse(

Diff for: commands2/paralleldeadlinegroup.py

+53-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
# validated: 2024-01-19 DS e07de37e64f2 ParallelDeadlineGroup.java
12
from __future__ import annotations
23

34
from typing import Dict
45

6+
from wpiutil import SendableBuilder
7+
58
from .command import Command, InterruptionBehavior
69
from .commandscheduler import CommandScheduler
710
from .exceptions import IllegalCommandUse
@@ -10,35 +13,66 @@
1013

1114
class ParallelDeadlineGroup(Command):
1215
"""
13-
A command composition that runs one of a selection of commands, either using a selector and a key
14-
to command mapping, or a supplier that returns the command directly at runtime.
16+
A command composition that runs a set of commands in parallel, ending only when a specific
17+
command (the "deadline") ends, interrupting all other commands that are still running at that
18+
point.
1519
1620
The rules for command compositions apply: command instances that are passed to it cannot be
1721
added to any other composition or scheduled individually, and the composition requires all
18-
subsystems its components require."""
22+
subsystems its components require.
23+
"""
1924

2025
def __init__(self, deadline: Command, *commands: Command):
2126
"""
22-
Creates a new SelectCommand.
27+
Creates a new ParallelDeadlineGroup. The given commands (including the
28+
deadline) will be executed simultaneously. The composition will finish when
29+
the deadline finishes, interrupting all other still-running commands. If
30+
the composition is interrupted, only the commands still running will be
31+
interrupted.
32+
33+
:param deadline: the command that determines when the composition ends
34+
:param commands: the commands to be executed
2335
24-
:param commands: the map of commands to choose from
25-
:param selector: the selector to determine which command to run"""
36+
:raises IllegalCommandUse: if the deadline command is also in the otherCommands argument
37+
"""
2638
super().__init__()
2739
self._commands: Dict[Command, bool] = {}
2840
self._runsWhenDisabled = True
2941
self._finished = True
30-
self._deadline = deadline
3142
self._interruptBehavior = InterruptionBehavior.kCancelIncoming
3243
self.addCommands(*commands)
33-
if deadline not in self._commands:
34-
self.addCommands(deadline)
44+
self.setDeadline(deadline)
3545

3646
def setDeadline(self, deadline: Command):
37-
if deadline not in self._commands:
38-
self.addCommands(deadline)
47+
"""
48+
Sets the deadline to the given command. The deadline is added to the group if it is not already
49+
contained.
50+
51+
:param deadline: the command that determines when the group ends
52+
53+
:raises IllegalCommandUse: if the deadline command is already in the composition
54+
"""
55+
56+
# use getattr here because deadline not set in constructor
57+
isAlreadyDeadline = deadline == getattr(self, "_deadline", None)
58+
if isAlreadyDeadline:
59+
return
60+
61+
if deadline in self._commands:
62+
raise IllegalCommandUse(
63+
f"The deadline command cannot also be in the other commands!"
64+
)
65+
self.addCommands(deadline)
3966
self._deadline = deadline
4067

4168
def addCommands(self, *commands: Command):
69+
"""
70+
Adds the given commands to the group.
71+
72+
:param commands: Commands to add to the group.
73+
74+
:raises IllegalCommandUse: if the deadline command is already in the composition
75+
"""
4276
commands = flatten_args_commands(commands)
4377
if not self._finished:
4478
raise IllegalCommandUse(
@@ -50,7 +84,7 @@ def addCommands(self, *commands: Command):
5084
for command in commands:
5185
if not command.getRequirements().isdisjoint(self.requirements):
5286
raise IllegalCommandUse(
53-
"Multiple comands in a parallel composition cannot require the same subsystems."
87+
"Multiple commands in a parallel composition cannot require the same subsystems."
5488
)
5589

5690
self._commands[command] = False
@@ -94,3 +128,10 @@ def runsWhenDisabled(self) -> bool:
94128

95129
def getInterruptionBehavior(self) -> InterruptionBehavior:
96130
return self._interruptBehavior
131+
132+
def initSendable(self, builder: SendableBuilder):
133+
super().initSendable(builder)
134+
135+
builder.addStringProperty(
136+
"deadline", lambda: self._deadline.getName(), lambda _: None
137+
)

Diff for: commands2/parallelracegroup.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS aaea85ff1656 ParallelRaceGroup.java
12
from __future__ import annotations
23

34
from typing import Set
@@ -15,23 +16,30 @@ class ParallelRaceGroup(Command):
1516
1617
The rules for command compositions apply: command instances that are passed to it cannot be
1718
added to any other composition or scheduled individually, and the composition requires all
18-
subsystems its components require."""
19+
subsystems its components require.
20+
"""
1921

2022
def __init__(self, *commands: Command):
2123
"""
2224
Creates a new ParallelCommandRace. The given commands will be executed simultaneously, and will
2325
"race to the finish" - the first command to finish ends the entire command, with all other
2426
commands being interrupted.
2527
26-
:param commands: the commands to include in this composition."""
28+
:param commands: the commands to include in this composition.
29+
"""
2730
super().__init__()
2831
self._commands: Set[Command] = set()
2932
self._runsWhenDisabled = True
30-
self._interruptBehavior = InterruptionBehavior.kCancelIncoming
3133
self._finished = True
34+
self._interruptBehavior = InterruptionBehavior.kCancelIncoming
3235
self.addCommands(*commands)
3336

3437
def addCommands(self, *commands: Command):
38+
"""
39+
Adds the given commands to the group.
40+
41+
:param commands: Commands to add to the group.
42+
"""
3543
commands = flatten_args_commands(commands)
3644
if not self._finished:
3745
raise IllegalCommandUse(

Diff for: commands2/pidcommand.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS f29a7d2e501b PIDCommand.java
12
# Copyright (c) FIRST and other WPILib contributors.
23
# Open Source Software; you can modify and/or share it under the terms of
34
# the WPILib BSD license file in the root directory of this project.
@@ -13,7 +14,7 @@
1314

1415
class PIDCommand(Command):
1516
"""
16-
A command that controls an output with a PIDController. Runs forever by default - to add
17+
A command that controls an output with a :class:`wpimath.controller.PIDController`. Runs forever by default - to add
1718
exit conditions and/or other behavior, subclass this class. The controller calculation and output
1819
are performed synchronously in the command's execute() method.
1920
"""
@@ -27,7 +28,7 @@ def __init__(
2728
*requirements: Subsystem,
2829
):
2930
"""
30-
Creates a new PIDCommand, which controls the given output with a PIDController.
31+
Creates a new PIDCommand, which controls the given output with a :class:`wpimath.controller.PIDController`.
3132
3233
:param controller: the controller that controls the output.
3334
:param measurementSource: the measurement of the process variable

Diff for: commands2/pidsubsystem.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# validated: 2024-01-19 DS f29a7d2e501b PIDSubsystem.java
12
# Copyright (c) FIRST and other WPILib contributors.
23
# Open Source Software; you can modify and/or share it under the terms of
34
# the WPILib BSD license file in the root directory of this project.
@@ -10,7 +11,7 @@
1011

1112
class PIDSubsystem(Subsystem):
1213
"""
13-
A subsystem that uses a {@link PIDController} to control an output. The
14+
A subsystem that uses a :class:`wpimath.controller.PIDController` to control an output. The
1415
controller is run synchronously from the subsystem's periodic() method.
1516
"""
1617

Diff for: commands2/printcommand.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
# validated: 2024-01-19 DS 8ac45f20bb47 PrintCommand.java
12
from __future__ import annotations
23

34
from .instantcommand import InstantCommand
45

56

67
class PrintCommand(InstantCommand):
78
"""
8-
A command that prints a string when initialized."""
9+
A command that prints a string when initialized.
10+
"""
911

1012
def __init__(self, message: str):
1113
"""
1214
Creates a new a PrintCommand.
1315
14-
:param message: the message to print"""
16+
:param message: the message to print
17+
"""
1518
super().__init__(lambda: print(message))
1619

1720
def runsWhenDisabled(self) -> bool:

0 commit comments

Comments
 (0)