001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj2.command.button; 006 007import edu.wpi.first.math.Pair; 008import edu.wpi.first.wpilibj.DriverStation.POVDirection; 009import edu.wpi.first.wpilibj.GenericHID; 010import edu.wpi.first.wpilibj.event.EventLoop; 011import edu.wpi.first.wpilibj2.command.CommandScheduler; 012import java.util.HashMap; 013import java.util.Map; 014 015/** 016 * A version of {@link GenericHID} with {@link Trigger} factories for command-based. 017 * 018 * @see GenericHID 019 */ 020public class CommandGenericHID { 021 private final GenericHID m_hid; 022 private final Map<EventLoop, Map<Integer, Trigger>> m_buttonCache = new HashMap<>(); 023 private final Map<EventLoop, Map<Pair<Integer, Double>, Trigger>> m_axisLessThanCache = 024 new HashMap<>(); 025 private final Map<EventLoop, Map<Pair<Integer, Double>, Trigger>> m_axisGreaterThanCache = 026 new HashMap<>(); 027 private final Map<EventLoop, Map<Pair<Integer, Double>, Trigger>> 028 m_axisMagnitudeGreaterThanCache = new HashMap<>(); 029 private final Map<EventLoop, Map<Integer, Trigger>> m_povCache = new HashMap<>(); 030 031 /** 032 * Construct an instance of a device. 033 * 034 * @param port The port index on the Driver Station that the device is plugged into. 035 */ 036 public CommandGenericHID(int port) { 037 m_hid = new GenericHID(port); 038 } 039 040 /** 041 * Get the underlying GenericHID object. 042 * 043 * @return the wrapped GenericHID object 044 */ 045 public GenericHID getHID() { 046 return m_hid; 047 } 048 049 /** 050 * Constructs an event instance around this button's digital signal. 051 * 052 * @param button the button index 053 * @return an event instance representing the button's digital signal attached to the {@link 054 * CommandScheduler#getDefaultButtonLoop() default scheduler button loop}. 055 * @see #button(int, EventLoop) 056 */ 057 public Trigger button(int button) { 058 return button(button, CommandScheduler.getInstance().getDefaultButtonLoop()); 059 } 060 061 /** 062 * Constructs an event instance around this button's digital signal. 063 * 064 * @param button the button index 065 * @param loop the event loop instance to attach the event to. 066 * @return an event instance representing the button's digital signal attached to the given loop. 067 */ 068 public Trigger button(int button, EventLoop loop) { 069 var cache = m_buttonCache.computeIfAbsent(loop, k -> new HashMap<>()); 070 return cache.computeIfAbsent(button, k -> new Trigger(loop, () -> m_hid.getRawButton(k))); 071 } 072 073 /** 074 * Constructs a Trigger instance based around this angle of the default (index 0) POV on the HID, 075 * attached to {@link CommandScheduler#getDefaultButtonLoop() the default command scheduler button 076 * loop}. 077 * 078 * @param angle POV angle. 079 * @return a Trigger instance based around this angle of a POV on the HID. 080 */ 081 public Trigger pov(POVDirection angle) { 082 return pov(0, angle, CommandScheduler.getInstance().getDefaultButtonLoop()); 083 } 084 085 /** 086 * Constructs a Trigger instance based around this angle of a POV on the HID. 087 * 088 * @param pov index of the POV to read (starting at 0). Defaults to 0. 089 * @param angle POV angle. 090 * @param loop the event loop instance to attach the event to. Defaults to {@link 091 * CommandScheduler#getDefaultButtonLoop() the default command scheduler button loop}. 092 * @return a Trigger instance based around this angle of a POV on the HID. 093 */ 094 public Trigger pov(int pov, POVDirection angle, EventLoop loop) { 095 var cache = m_povCache.computeIfAbsent(loop, k -> new HashMap<>()); 096 // angle.value is a 4 bit bitfield 097 return cache.computeIfAbsent( 098 pov * 16 + angle.value, k -> new Trigger(loop, () -> m_hid.getPOV(pov) == angle)); 099 } 100 101 /** 102 * Constructs a Trigger instance based around the up direction of the default (index 0) POV on the 103 * HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command scheduler 104 * button loop}. 105 * 106 * @return a Trigger instance based around the up direction of a POV on the HID. 107 */ 108 public Trigger povUp() { 109 return pov(POVDirection.Up); 110 } 111 112 /** 113 * Constructs a Trigger instance based around the up right direction of the default (index 0) POV 114 * on the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 115 * scheduler button loop}. 116 * 117 * @return a Trigger instance based around the up right direction of a POV on the HID. 118 */ 119 public Trigger povUpRight() { 120 return pov(POVDirection.UpRight); 121 } 122 123 /** 124 * Constructs a Trigger instance based around the right direction of the default (index 0) POV on 125 * the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 126 * scheduler button loop}. 127 * 128 * @return a Trigger instance based around the right direction of a POV on the HID. 129 */ 130 public Trigger povRight() { 131 return pov(POVDirection.Right); 132 } 133 134 /** 135 * Constructs a Trigger instance based around the down right direction of the default (index 0) 136 * POV on the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 137 * scheduler button loop}. 138 * 139 * @return a Trigger instance based around the down right direction of a POV on the HID. 140 */ 141 public Trigger povDownRight() { 142 return pov(POVDirection.DownRight); 143 } 144 145 /** 146 * Constructs a Trigger instance based around the down direction of the default (index 0) POV on 147 * the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 148 * scheduler button loop}. 149 * 150 * @return a Trigger instance based around the down direction of a POV on the HID. 151 */ 152 public Trigger povDown() { 153 return pov(POVDirection.Down); 154 } 155 156 /** 157 * Constructs a Trigger instance based around the down left POV direction of the default (index 0) 158 * POV on the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 159 * scheduler button loop}. 160 * 161 * @return a Trigger instance based around the down left POV direction of a POV on the HID. 162 */ 163 public Trigger povDownLeft() { 164 return pov(POVDirection.DownLeft); 165 } 166 167 /** 168 * Constructs a Trigger instance based around the left direction of the default (index 0) POV on 169 * the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 170 * scheduler button loop}. 171 * 172 * @return a Trigger instance based around the left direction of a POV on the HID. 173 */ 174 public Trigger povLeft() { 175 return pov(POVDirection.Left); 176 } 177 178 /** 179 * Constructs a Trigger instance based around the up left direction of the default (index 0) POV 180 * on the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 181 * scheduler button loop}. 182 * 183 * @return a Trigger instance based around the up left direction of a POV on the HID. 184 */ 185 public Trigger povUpLeft() { 186 return pov(POVDirection.UpLeft); 187 } 188 189 /** 190 * Constructs a Trigger instance based around the center (not pressed) position of the default 191 * (index 0) POV on the HID, attached to {@link CommandScheduler#getDefaultButtonLoop() the 192 * default command scheduler button loop}. 193 * 194 * @return a Trigger instance based around the center position of a POV on the HID. 195 */ 196 public Trigger povCenter() { 197 return pov(POVDirection.Center); 198 } 199 200 /** 201 * Constructs a Trigger instance that is true when the axis value is less than {@code threshold}, 202 * attached to {@link CommandScheduler#getDefaultButtonLoop() the default command scheduler button 203 * loop}. 204 * 205 * @param axis The axis to read, starting at 0 206 * @param threshold The value below which this trigger should return true. 207 * @return a Trigger instance that is true when the axis value is less than the provided 208 * threshold. 209 */ 210 public Trigger axisLessThan(int axis, double threshold) { 211 return axisLessThan(axis, threshold, CommandScheduler.getInstance().getDefaultButtonLoop()); 212 } 213 214 /** 215 * Constructs a Trigger instance that is true when the axis value is less than {@code threshold}, 216 * attached to the given loop. 217 * 218 * @param axis The axis to read, starting at 0 219 * @param threshold The value below which this trigger should return true. 220 * @param loop the event loop instance to attach the trigger to 221 * @return a Trigger instance that is true when the axis value is less than the provided 222 * threshold. 223 */ 224 public Trigger axisLessThan(int axis, double threshold, EventLoop loop) { 225 var cache = m_axisLessThanCache.computeIfAbsent(loop, k -> new HashMap<>()); 226 return cache.computeIfAbsent( 227 Pair.of(axis, threshold), k -> new Trigger(loop, () -> getRawAxis(axis) < threshold)); 228 } 229 230 /** 231 * Constructs a Trigger instance that is true when the axis value is less than {@code threshold}, 232 * attached to {@link CommandScheduler#getDefaultButtonLoop() the default command scheduler button 233 * loop}. 234 * 235 * @param axis The axis to read, starting at 0 236 * @param threshold The value above which this trigger should return true. 237 * @return a Trigger instance that is true when the axis value is greater than the provided 238 * threshold. 239 */ 240 public Trigger axisGreaterThan(int axis, double threshold) { 241 return axisGreaterThan(axis, threshold, CommandScheduler.getInstance().getDefaultButtonLoop()); 242 } 243 244 /** 245 * Constructs a Trigger instance that is true when the axis value is greater than {@code 246 * threshold}, attached to the given loop. 247 * 248 * @param axis The axis to read, starting at 0 249 * @param threshold The value above which this trigger should return true. 250 * @param loop the event loop instance to attach the trigger to. 251 * @return a Trigger instance that is true when the axis value is greater than the provided 252 * threshold. 253 */ 254 public Trigger axisGreaterThan(int axis, double threshold, EventLoop loop) { 255 var cache = m_axisGreaterThanCache.computeIfAbsent(loop, k -> new HashMap<>()); 256 return cache.computeIfAbsent( 257 Pair.of(axis, threshold), k -> new Trigger(loop, () -> getRawAxis(axis) > threshold)); 258 } 259 260 /** 261 * Constructs a Trigger instance that is true when the axis magnitude value is greater than {@code 262 * threshold}, attached to the given loop. 263 * 264 * @param axis The axis to read, starting at 0 265 * @param threshold The value above which this trigger should return true. 266 * @param loop the event loop instance to attach the trigger to. 267 * @return a Trigger instance that is true when the axis magnitude value is greater than the 268 * provided threshold. 269 */ 270 public Trigger axisMagnitudeGreaterThan(int axis, double threshold, EventLoop loop) { 271 var cache = m_axisMagnitudeGreaterThanCache.computeIfAbsent(loop, k -> new HashMap<>()); 272 return cache.computeIfAbsent( 273 Pair.of(axis, threshold), 274 k -> new Trigger(loop, () -> Math.abs(getRawAxis(axis)) > threshold)); 275 } 276 277 /** 278 * Constructs a Trigger instance that is true when the axis magnitude value is greater than {@code 279 * threshold}, attached to {@link CommandScheduler#getDefaultButtonLoop() the default command 280 * scheduler button loop}. 281 * 282 * @param axis The axis to read, starting at 0 283 * @param threshold The value above which this trigger should return true. 284 * @return a Trigger instance that is true when the deadbanded axis value is active (non-zero). 285 */ 286 public Trigger axisMagnitudeGreaterThan(int axis, double threshold) { 287 return axisMagnitudeGreaterThan( 288 axis, threshold, CommandScheduler.getInstance().getDefaultButtonLoop()); 289 } 290 291 /** 292 * Get the value of the axis. 293 * 294 * @param axis The axis to read, starting at 0. 295 * @return The value of the axis. 296 */ 297 public double getRawAxis(int axis) { 298 return m_hid.getRawAxis(axis); 299 } 300 301 /** 302 * Set the rumble output for the HID. The DS currently supports 2 rumble values, left rumble and 303 * right rumble. 304 * 305 * @param type Which rumble value to set 306 * @param value The normalized value (0 to 1) to set the rumble to 307 */ 308 public void setRumble(GenericHID.RumbleType type, double value) { 309 m_hid.setRumble(type, value); 310 } 311 312 /** 313 * Get if the HID is connected. 314 * 315 * @return true if the HID is connected 316 */ 317 public boolean isConnected() { 318 return m_hid.isConnected(); 319 } 320}