|
12 | 12 |
|
13 | 13 | import java.util.Optional;
|
14 | 14 |
|
| 15 | +/** |
| 16 | + * A video-game-style VEX V5 controller, with two joysticks and a variety of buttons. |
| 17 | + * <p> |
| 18 | + * This class allows you to read from the buttons and joysticks on the controller and write to the controller’s display. |
| 19 | + * Controller instances are accessible through the {@link Peripherals} class. |
| 20 | + */ |
15 | 21 | public class Controller {
|
16 | 22 | private final @NotNull Id id;
|
17 | 23 | private @NotNull State previousState = State.empty();
|
18 | 24 |
|
| 25 | + /** |
| 26 | + * Creates a new controller instance. |
| 27 | + * |
| 28 | + * @param ignoredKey the peripherals key, which may only be accessed from within the {@link Peripherals} class |
| 29 | + * @param type the type of controller to create |
| 30 | + * @see Peripherals#takeController(Controller.Id) |
| 31 | + */ |
19 | 32 | @ApiStatus.Internal
|
20 | 33 | public Controller(@NotNull Peripherals.Key ignoredKey, @NotNull Id type) {
|
21 | 34 | this.id = type;
|
22 | 35 | }
|
23 | 36 |
|
| 37 | + /** |
| 38 | + * Gets the ID of this controller - whether it is the primary or partner controller. |
| 39 | + * |
| 40 | + * @return the controller ID |
| 41 | + */ |
24 | 42 | public @NotNull Controller.Id getId() {
|
25 | 43 | return id;
|
26 | 44 | }
|
27 | 45 |
|
| 46 | + /** |
| 47 | + * Checks if this controller is connected to the robot brain. |
| 48 | + * |
| 49 | + * @return {@code true} if the controller is connected, {@code false} otherwise |
| 50 | + */ |
28 | 51 | public boolean connected() {
|
29 | 52 | var status = VexSdk.Controller.vexControllerConnectionStatusGet(id.raw());
|
30 | 53 | return !status.equals(V5_ControllerStatus.kV5ControllerOffline);
|
31 | 54 | }
|
32 | 55 |
|
| 56 | + /** |
| 57 | + * Gets the latest data from the controller. |
| 58 | + * |
| 59 | + * @return the controller state, if the controller is connected and the robot is in driver control mode |
| 60 | + */ |
33 | 61 | public @NotNull Optional<State> getState() {
|
34 | 62 | if (!CompetitionRuntime.mode().equals(CompetitionRuntime.Mode.Driver) || !connected()) {
|
35 | 63 | return Optional.empty();
|
36 | 64 | }
|
37 |
| - var state = new State( |
38 |
| - new JoystickState( |
39 |
| - (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaLeftX), |
40 |
| - (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaLeftY) |
41 |
| - ), |
42 |
| - new JoystickState( |
43 |
| - (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaRightX), |
44 |
| - (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaRightY) |
45 |
| - ), |
46 |
| - new ButtonState( |
47 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonA) == 1, |
48 |
| - previousState.a().pressed() |
49 |
| - ), |
50 |
| - new ButtonState( |
51 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonB) == 1, |
52 |
| - previousState.b().pressed() |
53 |
| - ), |
54 |
| - new ButtonState( |
55 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonX) == 1, |
56 |
| - previousState.x().pressed() |
57 |
| - ), |
58 |
| - new ButtonState( |
59 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonY) == 1, |
60 |
| - previousState.y().pressed() |
61 |
| - ), |
62 |
| - new ButtonState( |
63 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonUp) == 1, |
64 |
| - previousState.up().pressed() |
65 |
| - ), |
66 |
| - new ButtonState( |
67 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonDown) == 1, |
68 |
| - previousState.down().pressed() |
69 |
| - ), |
70 |
| - new ButtonState( |
71 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonLeft) == 1, |
72 |
| - previousState.left().pressed() |
73 |
| - ), |
74 |
| - new ButtonState( |
75 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonRight) == 1, |
76 |
| - previousState.right().pressed() |
77 |
| - ), |
78 |
| - new ButtonState( |
79 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonL1) == 1, |
80 |
| - previousState.l1().pressed() |
81 |
| - ), |
82 |
| - new ButtonState( |
83 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonL2) == 1, |
84 |
| - previousState.l2().pressed() |
85 |
| - ), |
86 |
| - new ButtonState( |
87 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonR1) == 1, |
88 |
| - previousState.r1().pressed() |
89 |
| - ), |
90 |
| - new ButtonState( |
91 |
| - VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonR2) == 1, |
92 |
| - previousState.r2().pressed() |
93 |
| - ) |
94 |
| - ); |
| 65 | + var state = new State(new JoystickState((byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaLeftX), (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaLeftY)), new JoystickState((byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaRightX), (byte) VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.AnaRightY)), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonA) == 1, previousState.a().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonB) == 1, previousState.b().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonX) == 1, previousState.x().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonY) == 1, previousState.y().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonUp) == 1, previousState.up().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonDown) == 1, previousState.down().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonLeft) == 1, previousState.left().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonRight) == 1, previousState.right().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonL1) == 1, previousState.l1().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonL2) == 1, previousState.l2().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonR1) == 1, previousState.r1().pressed()), new ButtonState(VexSdk.Controller.vexControllerGet(id.raw(), V5_ControllerIndex.ButtonR2) == 1, previousState.r2().pressed())); |
95 | 66 | previousState = state;
|
96 | 67 | return Optional.of(state);
|
97 | 68 | }
|
98 | 69 |
|
| 70 | + /** |
| 71 | + * A unique identifier for one of the two V5 controllers that may be connected to the robot brain. |
| 72 | + */ |
99 | 73 | public enum Id {
|
| 74 | + /** |
| 75 | + * The primary controller, also known as the "master" controller. This controller is directly connected to the |
| 76 | + * robot brain via a radio or wired connection. |
| 77 | + */ |
100 | 78 | Primary,
|
| 79 | + /** |
| 80 | + * The partner controller. This controller is connected to the primary controller via a wired connection. |
| 81 | + */ |
101 | 82 | Partner;
|
102 | 83 |
|
103 |
| - @NotNull |
104 |
| - V5_ControllerId raw() { |
| 84 | + /** |
| 85 | + * Gets the internal representation of this controller ID. |
| 86 | + * |
| 87 | + * @return the raw controller ID value |
| 88 | + */ |
| 89 | + @NotNull V5_ControllerId raw() { |
105 | 90 | return switch (this) {
|
106 | 91 | case Primary -> V5_ControllerId.kControllerMaster;
|
107 | 92 | case Partner -> V5_ControllerId.kControllerPartner;
|
108 | 93 | };
|
109 | 94 | }
|
110 | 95 | }
|
111 | 96 |
|
112 |
| - public record State( |
113 |
| - @NotNull JoystickState leftStick, |
114 |
| - @NotNull JoystickState rightStick, |
115 |
| - @NotNull ButtonState a, |
116 |
| - @NotNull ButtonState b, |
117 |
| - @NotNull ButtonState x, |
118 |
| - @NotNull ButtonState y, |
119 |
| - @NotNull ButtonState up, |
120 |
| - @NotNull ButtonState down, |
121 |
| - @NotNull ButtonState left, |
122 |
| - @NotNull ButtonState right, |
123 |
| - @NotNull ButtonState l1, |
124 |
| - @NotNull ButtonState l2, |
125 |
| - @NotNull ButtonState r1, |
126 |
| - @NotNull ButtonState r2 |
127 |
| - ) { |
| 97 | + /** |
| 98 | + * The current state of the controller, including the positions of the joysticks and the states of the buttons. |
| 99 | + * |
| 100 | + * @param leftStick the state of the left joystick |
| 101 | + * @param rightStick the state of the right joystick |
| 102 | + * @param a the state of the A button |
| 103 | + * @param b the state of the B button |
| 104 | + * @param x the state of the X button |
| 105 | + * @param y the state of the Y button |
| 106 | + * @param up the state of the up button |
| 107 | + * @param down the state of the down button |
| 108 | + * @param left the state of the left button |
| 109 | + * @param right the state of the right button |
| 110 | + * @param l1 the state of the L1 button |
| 111 | + * @param l2 the state of the L2 button |
| 112 | + * @param r1 the state of the R1 button |
| 113 | + * @param r2 the state of the R2 button |
| 114 | + */ |
| 115 | + public record State(@NotNull JoystickState leftStick, @NotNull JoystickState rightStick, @NotNull ButtonState a, |
| 116 | + @NotNull ButtonState b, @NotNull ButtonState x, @NotNull ButtonState y, @NotNull ButtonState up, |
| 117 | + @NotNull ButtonState down, @NotNull ButtonState left, @NotNull ButtonState right, |
| 118 | + @NotNull ButtonState l1, @NotNull ButtonState l2, @NotNull ButtonState r1, |
| 119 | + @NotNull ButtonState r2) { |
| 120 | + /** |
| 121 | + * Gets an empty controller state, with all buttons and joysticks in their default positions. |
| 122 | + * <p> |
| 123 | + * This state is useful as a fallback when the controller is not connected or the robot is not in driver control |
| 124 | + * mode: |
| 125 | + * <pre>{@code |
| 126 | + * Controller controller = peripherals.takeController(Controller.Id.Primary); |
| 127 | + * var state = controller.getState().orElseGet(Controller.State::empty); |
| 128 | + * }</pre> |
| 129 | + * |
| 130 | + * @return an empty controller state |
| 131 | + */ |
128 | 132 | @Contract(value = "-> new", pure = true)
|
129 | 133 | public static @NotNull State empty() {
|
130 |
| - return new State( |
131 |
| - JoystickState.empty(), |
132 |
| - JoystickState.empty(), |
133 |
| - ButtonState.empty(), |
134 |
| - ButtonState.empty(), |
135 |
| - ButtonState.empty(), |
136 |
| - ButtonState.empty(), |
137 |
| - ButtonState.empty(), |
138 |
| - ButtonState.empty(), |
139 |
| - ButtonState.empty(), |
140 |
| - ButtonState.empty(), |
141 |
| - ButtonState.empty(), |
142 |
| - ButtonState.empty(), |
143 |
| - ButtonState.empty(), |
144 |
| - ButtonState.empty() |
145 |
| - ); |
| 134 | + return new State(JoystickState.empty(), JoystickState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty(), ButtonState.empty()); |
146 | 135 | }
|
147 | 136 | }
|
148 | 137 |
|
149 |
| - public record JoystickState( |
150 |
| - byte rawX, |
151 |
| - byte rawY |
152 |
| - ) { |
| 138 | + /** |
| 139 | + * The current state of a joystick, including the raw X and Y values and their normalized values. |
| 140 | + * |
| 141 | + * @param rawX the raw X value of the joystick, ranging from -127 to 127 |
| 142 | + * @param rawY the raw Y value of the joystick, ranging from -127 to 127 |
| 143 | + */ |
| 144 | + public record JoystickState(byte rawX, byte rawY) { |
| 145 | + /** |
| 146 | + * Gets an empty joystick state, with both the X and Y values set to 0. |
| 147 | + * |
| 148 | + * @return an empty joystick state |
| 149 | + * @see State#empty() |
| 150 | + */ |
153 | 151 | @Contract(value = "-> new", pure = true)
|
154 | 152 | public static @NotNull JoystickState empty() {
|
155 | 153 | return new JoystickState((byte) 0, (byte) 0);
|
156 | 154 | }
|
157 | 155 |
|
| 156 | + /** |
| 157 | + * Gets the normalized X value of the joystick, ranging from -1.0 to 1.0. |
| 158 | + * |
| 159 | + * @return the normalized X value |
| 160 | + */ |
158 | 161 | public double getX() {
|
159 | 162 | return rawX / 127.0;
|
160 | 163 | }
|
161 | 164 |
|
| 165 | + /** |
| 166 | + * Gets the normalized Y value of the joystick, ranging from -1.0 to 1.0. |
| 167 | + * |
| 168 | + * @return the normalized Y value |
| 169 | + */ |
162 | 170 | public double getY() {
|
163 | 171 | return rawY / 127.0;
|
164 | 172 | }
|
165 | 173 | }
|
166 | 174 |
|
167 |
| - public record ButtonState( |
168 |
| - boolean pressed, |
169 |
| - boolean previousPressed |
170 |
| - ) { |
| 175 | + /** |
| 176 | + * The current state of a button, including whether it is currently pressed and whether it was pressed when the |
| 177 | + * previous controller state was read. |
| 178 | + * |
| 179 | + * @param pressed whether the button is currently pressed |
| 180 | + * @param previouslyPressed whether the button was pressed when the controller was last read from |
| 181 | + */ |
| 182 | + public record ButtonState(boolean pressed, boolean previouslyPressed) { |
| 183 | + /** |
| 184 | + * Gets an empty button state, with the button both currently not pressed and previously not pressed. |
| 185 | + * |
| 186 | + * @return an empty button state |
| 187 | + * @see State#empty() |
| 188 | + */ |
171 | 189 | @Contract(value = "-> new", pure = true)
|
172 | 190 | public static @NotNull ButtonState empty() {
|
173 | 191 | return new ButtonState(false, false);
|
174 | 192 | }
|
175 | 193 |
|
| 194 | + /** |
| 195 | + * Checks if the button is currently released (not pressed). |
| 196 | + * |
| 197 | + * @return {@code true} if the button is released, {@code false} if it is pressed |
| 198 | + */ |
176 | 199 | public boolean released() {
|
177 | 200 | return !pressed;
|
178 | 201 | }
|
179 | 202 |
|
| 203 | + /** |
| 204 | + * Checks if the button used to be released, but is now pressed. This is useful for triggering actions when a |
| 205 | + * button is pressed, but not continuously running those actions while the button is held down. |
| 206 | + * <p> |
| 207 | + * For example, you might use this method to toggle a boolean value each time a button is pressed: |
| 208 | + * <pre>{@code |
| 209 | + * var state = controller.getState().orElseGet(Controller.State::empty); |
| 210 | + * boolean toggle = false; |
| 211 | + * if (state.a().isNowPressed()) { |
| 212 | + * toggle = !toggle; |
| 213 | + * System.out.println("Toggled: " + toggle); |
| 214 | + * } |
| 215 | + * }</pre> |
| 216 | + * |
| 217 | + * @return {@code true} if the button was released and is now pressed, {@code false} otherwise |
| 218 | + * @see #pressed() |
| 219 | + * @see #isNowReleased() |
| 220 | + */ |
180 | 221 | public boolean isNowPressed() {
|
181 |
| - return pressed && !previousPressed; |
| 222 | + return pressed && !previouslyPressed; |
182 | 223 | }
|
183 | 224 |
|
| 225 | + /** |
| 226 | + * Checks if the button used to be pressed, but is now released. This is useful for triggering actions when a |
| 227 | + * button is released, but not continuously running those actions until the button is pressed again. |
| 228 | + * |
| 229 | + * @return {@code true} if the button was pressed and is now released, {@code false} otherwise |
| 230 | + * @see #released() |
| 231 | + * @see #isNowPressed() |
| 232 | + */ |
184 | 233 | public boolean isNowReleased() {
|
185 |
| - return !pressed && previousPressed; |
| 234 | + return !pressed && previouslyPressed; |
186 | 235 | }
|
187 | 236 | }
|
188 | 237 | }
|
0 commit comments