mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
[cmd3] Enforce command lifetimes across all opmode and command scopes (#8705)
Commands are no longer able to outlive their schedule-site's scope,
regardless of how they were scheduled (set as a default command, bound
to a trigger, or manually scheduled)
As a consequence, default commands need better tracking so the default
command setting can be released when their scope exits and the next-most
appropriate default command can be rescheduled (eg, an opmode sets a
default command, then the globally-scoped default is restored when the
opmode exits). Some complexity is required here to make it work well for
edge cases.
Like `schedule()`, `setDefaultCommand()` will immediately start the new
default command if called inside of another command to avoid 1-loop
delays. However, this does not apply when called by the _current_
default command, as it would result in attempting to cancel the default
command while it's mounted (which is impossible and would throw an
exception)
```java
class Robot extends OpModeRobot {
final Drive drive = new Drive();
final CommandXboxController controller = new CommandXboxController(1);
public Robot() {
// global default command, active unless overridden in an opmode or command
drive.setDefaultCommand(drive.stop());
// global trigger binding, always active
controller.rightBumper().onTrue(drive.setX());
}
}
@Teleop
class ExampleOpMode extends PeriodicOpMode {
public ExampleOpMode(Robot robot) {
// opmode-specific default command
robot.drive.setDefaultCommand(robot.drive.operatorControl(robot.controller));
// opmode-specific binding
robot.controller.leftBumper().whileTrue(robot.drive.stop());
// opmode-specific binding that takes precedence over the global binding
// because it happens last; it "wins out" over the `setX()` binding
robot.controller.rightBumper().onTrue(robot.drive.selfTest());
}
@Override
public void periodic() {
Scheduler.getDefault().run();
}
}
```
This commit is contained in:
@@ -8,6 +8,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
@@ -26,6 +28,34 @@ class DriverStationTest {
|
||||
assertEquals(expected, DriverStation.isJoystickConnected(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOpmodeIdReturnsZeroUntilUserProgramStarts() {
|
||||
DriverStationSim.setOpMode(0x1234);
|
||||
DriverStationSim.notifyNewData();
|
||||
assertEquals(0, DriverStation.getOpModeId());
|
||||
|
||||
DriverStation.observeUserProgramStarting();
|
||||
// need to manually mask because the upper eight bits include robot mode information
|
||||
assertEquals(0x1234, DriverStation.getOpModeId() & 0xFFFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOpmodeReturnsEmptyStringUntilUserProgramStarts() {
|
||||
DriverStationSim.setOpMode(0x1234);
|
||||
DriverStationSim.notifyNewData();
|
||||
assertEquals("", DriverStation.getOpMode());
|
||||
|
||||
DriverStation.observeUserProgramStarting();
|
||||
// in Sim, the opmode string is just the stringified version of the opmode i64 "<0000...0000>"
|
||||
// we need to parse the string to get the
|
||||
// need to manually mask because the upper eight bits include robot mode information
|
||||
String opmodeName = DriverStation.getOpMode();
|
||||
assertEquals(
|
||||
"0x1234",
|
||||
String.format(
|
||||
"0x%x", Long.parseLong(opmodeName.substring(1, opmodeName.length() - 1)) & 0xFFFF));
|
||||
}
|
||||
|
||||
static Stream<Arguments> isConnectedProvider() {
|
||||
return Stream.of(
|
||||
arguments(0, 0, 0, false),
|
||||
@@ -52,4 +82,12 @@ class DriverStationTest {
|
||||
arguments(true, true, false),
|
||||
arguments(true, false, false));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@SuppressWarnings("PMD.AvoidAccessibilityAlteration")
|
||||
void resetUserProgramFlag() throws ReflectiveOperationException {
|
||||
var field = DriverStation.class.getDeclaredField("m_userProgramStarted");
|
||||
field.setAccessible(true);
|
||||
field.set(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user