Skip to content

Commit 0d9f6c4

Browse files
authored
Merge pull request #43 from DeepBlueRobotics/safe-mode
Safe Mode Utilities
2 parents 1be3fcf + c43a679 commit 0d9f6c4

10 files changed

Lines changed: 972 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.carlmontrobotics.lib199.safeMode;
2+
3+
import edu.wpi.first.wpilibj2.command.Command;
4+
import edu.wpi.first.wpilibj2.command.CommandScheduler;
5+
import edu.wpi.first.wpilibj2.command.FunctionalCommand;
6+
import edu.wpi.first.wpilibj2.command.Subsystem;
7+
8+
/**
9+
* A command that only runs when safe-mode is enabled and returns {@code isFinished() = true} otherwise.
10+
*
11+
* Note that this does not block calls to {@link #end(boolean)} (If the command is scheduled when safe-mode is disabled, {@link #end(boolean)} will be called immediately})
12+
*/
13+
public class SafeCommand extends FunctionalCommand {
14+
15+
private final Command command;
16+
17+
/**
18+
* Creates a new SafeCommand
19+
* @param command The command to run
20+
*/
21+
public SafeCommand(Command command) {
22+
super(
23+
() -> { if(SafeMode.isEnabled()) command.initialize(); },
24+
() -> { if(SafeMode.isEnabled()) command.execute(); },
25+
command::end,
26+
() -> command.isFinished() || !SafeMode.isEnabled(),
27+
command.getRequirements().toArray(Subsystem[]::new)
28+
);
29+
CommandScheduler.getInstance().registerComposedCommands(this.command = command);
30+
}
31+
32+
@Override
33+
public boolean runsWhenDisabled() {
34+
return command.runsWhenDisabled();
35+
}
36+
37+
@Override
38+
public InterruptionBehavior getInterruptionBehavior() {
39+
return command.getInterruptionBehavior();
40+
}
41+
42+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.carlmontrobotics.lib199.safeMode;
2+
3+
import edu.wpi.first.wpilibj2.command.Command;
4+
import edu.wpi.first.wpilibj2.command.CommandScheduler;
5+
import edu.wpi.first.wpilibj2.command.FunctionalCommand;
6+
import edu.wpi.first.wpilibj2.command.Subsystem;
7+
8+
/**
9+
* A command that only runs its {@link #execute()} method when safe-mode is enabled, and continues running as long as the underlying command is not finished.
10+
*
11+
* Keep in mind that this does not block calls to {@link #initialize()} or {@link #end(boolean)}, so it is not appropriate for wrapping commands such as {@link edu.wpi.first.wpilibj2.command.InstantCommand}.
12+
* It is intended for cases where the command should keep running such as a default command where {@link #isFinished()} must always return {@code false}
13+
*/
14+
public class SafeExecuteBlockingCommand extends FunctionalCommand {
15+
16+
private final Command command;
17+
18+
public SafeExecuteBlockingCommand(Command command) {
19+
super(
20+
() -> command.initialize(),
21+
() -> { if(SafeMode.isEnabled()) command.execute(); },
22+
command::end,
23+
command::isFinished,
24+
command.getRequirements().toArray(Subsystem[]::new)
25+
);
26+
CommandScheduler.getInstance().registerComposedCommands(this.command = command);
27+
}
28+
29+
@Override
30+
public boolean runsWhenDisabled() {
31+
return command.runsWhenDisabled();
32+
}
33+
34+
@Override
35+
public InterruptionBehavior getInterruptionBehavior() {
36+
return command.getInterruptionBehavior();
37+
}
38+
39+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.carlmontrobotics.lib199.safeMode;
2+
3+
import java.util.Map;
4+
import java.util.Set;
5+
6+
import edu.wpi.first.wpilibj.GenericHID;
7+
8+
/**
9+
* A wrapper for a {@link GenericHID} that implements safe-mode features.
10+
*
11+
* This class is designed for internal use via {@link org.carlmontrobotics.lib199.Mocks}.
12+
* As such it provides re-implementations for {@link GenericHID} methods without extending {@link GenericHID}
13+
* to allow for the extension of subclasses such as {@link edu.wpi.first.wpilibj.Joystick}, {@link edu.wpi.first.wpilibj.PS4Controller},
14+
* {@link edu.wpi.first.wpilibj.XboxController}, etc. To wrap a joystick, call {@link SafeMode#makeSafe(GenericHID)}.
15+
*/
16+
public class SafeJoystick {
17+
18+
/**
19+
* The unsafe joystick that this class wraps.
20+
*/
21+
public final GenericHID unsafeJoystick;
22+
23+
private final Set<Integer> safeDisabledButtons;
24+
private final Set<Integer> safeDisabledAxes;
25+
private final Map<Integer, Double> safeScaledAxes;
26+
private final Map<Integer, Set<Integer>> safeDisabledPOV;
27+
28+
/**
29+
* Creates a new SafeJoystick
30+
*
31+
* @param unsafeJoystick The unsafe joystick to wrap
32+
* @param safeDisabledButtons The buttons that should be disabled in safe-mode
33+
* @param safeDisabledAxes The axes that should be disabled in safe-mode
34+
* @param safeScaledAxes The axes that should be scaled in safe-mode (key: axis, value: scale factor)
35+
* @param safeDisabledPOV The POVs that should be disabled in safe-mode (key: POV, value: set of disabled values)
36+
*/
37+
public SafeJoystick(GenericHID unsafeJoystick, Set<Integer> safeDisabledButtons, Set<Integer> safeDisabledAxes, Map<Integer, Double> safeScaledAxes, Map<Integer, Set<Integer>> safeDisabledPOV) {
38+
this.unsafeJoystick = unsafeJoystick;
39+
this.safeDisabledButtons = safeDisabledButtons;
40+
this.safeDisabledAxes = safeDisabledAxes;
41+
this.safeScaledAxes = safeScaledAxes;
42+
this.safeDisabledPOV = safeDisabledPOV;
43+
}
44+
45+
// All other methods always fall through to these five
46+
47+
/**
48+
* Safe version of {@link GenericHID#getRawButton(int)}.
49+
*
50+
* @param button The button to read
51+
* @return The state of the button
52+
*/
53+
public boolean getRawButton(int button) {
54+
if (SafeMode.isEnabled() && safeDisabledButtons.contains(button)) {
55+
return false;
56+
} else {
57+
return unsafeJoystick.getRawButton(button);
58+
}
59+
}
60+
61+
/**
62+
* Safe version of {@link GenericHID#getRawButtonPressed(int)}.
63+
*
64+
* @param button The button to read
65+
* @return Whether the button was pressed since the last check
66+
*/
67+
public boolean getRawButtonPressed(int button) {
68+
if (SafeMode.isEnabled() && safeDisabledButtons.contains(button)) {
69+
return false;
70+
} else {
71+
return unsafeJoystick.getRawButtonPressed(button);
72+
}
73+
}
74+
75+
/**
76+
* Safe version of {@link GenericHID#getRawButtonReleased(int)}.
77+
*
78+
* @param button The button to read
79+
* @return Whether the button was released since the last check
80+
*/
81+
public boolean getRawButtonReleased(int button) {
82+
if (SafeMode.isEnabled() && safeDisabledButtons.contains(button)) {
83+
return false;
84+
} else {
85+
return unsafeJoystick.getRawButtonReleased(button);
86+
}
87+
}
88+
89+
/**
90+
* Safe version of {@link GenericHID#getRawAxis(int)}.
91+
*
92+
* @param axis The axis to read
93+
* @return The value of the axis
94+
*/
95+
public double getRawAxis(int axis) {
96+
if (SafeMode.isEnabled() && safeDisabledAxes.contains(axis)) {
97+
return 0;
98+
} else if (SafeMode.isEnabled() && safeScaledAxes.containsKey(axis)) {
99+
return unsafeJoystick.getRawAxis(axis) * safeScaledAxes.get(axis);
100+
} else {
101+
return unsafeJoystick.getRawAxis(axis);
102+
}
103+
}
104+
105+
/**
106+
* Safe version of {@link GenericHID#getPOV(int)}.
107+
*
108+
* @param pov The POV to read
109+
* @return The value of the POV
110+
*/
111+
public double getPOV(int pov) {
112+
if (SafeMode.isEnabled() && safeDisabledPOV.containsKey(pov) && safeDisabledPOV.get(pov).contains(unsafeJoystick.getPOV(pov))) {
113+
return -1;
114+
} else {
115+
return unsafeJoystick.getPOV(pov);
116+
}
117+
}
118+
119+
}

0 commit comments

Comments
 (0)