Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/ |
| 2 | /* Copyright (c) FIRST 2008-2017. All Rights Reserved. */ |
| 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ |
| 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ |
| 5 | /* the project. */ |
| 6 | /*----------------------------------------------------------------------------*/ |
| 7 | |
| 8 | #include "frc971/wpilib/ahal/DriverStation.h" |
| 9 | |
| 10 | #include <chrono> |
| 11 | |
| 12 | #include "FRC_NetworkCommunication/FRCComm.h" |
Brian Silverman | f819b44 | 2019-01-20 16:51:04 -0800 | [diff] [blame] | 13 | #include "aos/make_unique.h" |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 14 | #include "frc971/wpilib/ahal/AnalogInput.h" |
| 15 | #include "frc971/wpilib/ahal/Utility.h" |
| 16 | #include "frc971/wpilib/ahal/WPIErrors.h" |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 17 | #include "hal/HAL.h" |
| 18 | #include "hal/Power.h" |
| 19 | #include "wpi/SmallString.h" |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 20 | |
| 21 | using namespace frc; |
| 22 | |
| 23 | #define WPI_LIB_FATAL(error_tag) \ |
| 24 | do { \ |
| 25 | } while (false) |
| 26 | |
| 27 | const int DriverStation::kJoystickPorts; |
| 28 | |
| 29 | DriverStation::~DriverStation() {} |
| 30 | |
| 31 | /** |
| 32 | * Return a pointer to the singleton DriverStation. |
| 33 | * |
| 34 | * @return Pointer to the DS instance |
| 35 | */ |
| 36 | DriverStation &DriverStation::GetInstance() { |
| 37 | static DriverStation instance; |
| 38 | return instance; |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Report an error to the DriverStation messages window. |
| 43 | * |
| 44 | * The error is also printed to the program console. |
| 45 | */ |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 46 | void DriverStation::ReportError(const wpi::Twine &error) { |
| 47 | wpi::SmallString<128> temp; |
| 48 | HAL_SendError(1, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "", |
| 49 | 1); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Report a warning to the DriverStation messages window. |
| 54 | * |
| 55 | * The warning is also printed to the program console. |
| 56 | */ |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 57 | void DriverStation::ReportWarning(const wpi::Twine &error) { |
| 58 | wpi::SmallString<128> temp; |
| 59 | HAL_SendError(0, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "", |
| 60 | 1); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Report an error to the DriverStation messages window. |
| 65 | * |
| 66 | * The error is also printed to the program console. |
| 67 | */ |
| 68 | void DriverStation::ReportError(bool is_error, int32_t code, |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 69 | const wpi::Twine &error, |
| 70 | const wpi::Twine &location, |
| 71 | const wpi::Twine &stack) { |
| 72 | wpi::SmallString<128> errorTemp; |
| 73 | wpi::SmallString<128> locationTemp; |
| 74 | wpi::SmallString<128> stackTemp; |
| 75 | HAL_SendError(is_error, code, 0, |
| 76 | error.toNullTerminatedStringRef(errorTemp).data(), |
| 77 | location.toNullTerminatedStringRef(locationTemp).data(), |
| 78 | stack.toNullTerminatedStringRef(stackTemp).data(), 1); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Get the value of the axis on a joystick. |
| 83 | * |
| 84 | * This depends on the mapping of the joystick connected to the specified port. |
| 85 | * |
| 86 | * @param stick The joystick to read. |
| 87 | * @param axis The analog axis value to read from the joystick. |
| 88 | * @return The value of the axis on the joystick. |
| 89 | */ |
| 90 | double DriverStation::GetStickAxis(int stick, int axis) { |
| 91 | if (stick >= kJoystickPorts) { |
| 92 | WPI_LIB_FATAL(BadJoystickIndex); |
| 93 | return 0; |
| 94 | } |
| 95 | if (axis >= m_joystickAxes[stick].count) { |
| 96 | if (axis >= HAL_kMaxJoystickAxes) WPI_LIB_FATAL(BadJoystickAxis); |
| 97 | return 0.0; |
| 98 | } |
| 99 | |
| 100 | return m_joystickAxes[stick].axes[axis]; |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Get the state of a POV on the joystick. |
| 105 | * |
| 106 | * @return the angle of the POV in degrees, or -1 if the POV is not pressed. |
| 107 | */ |
| 108 | int DriverStation::GetStickPOV(int stick, int pov) { |
| 109 | if (stick >= kJoystickPorts) { |
| 110 | WPI_LIB_FATAL(BadJoystickIndex); |
| 111 | return -1; |
| 112 | } |
| 113 | if (pov >= m_joystickPOVs[stick].count) { |
| 114 | if (pov >= HAL_kMaxJoystickPOVs) WPI_LIB_FATAL(BadJoystickAxis); |
| 115 | return -1; |
| 116 | } |
| 117 | |
| 118 | return m_joystickPOVs[stick].povs[pov]; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * The state of the buttons on the joystick. |
| 123 | * |
| 124 | * @param stick The joystick to read. |
| 125 | * @return The state of the buttons on the joystick. |
| 126 | */ |
| 127 | int DriverStation::GetStickButtons(int stick) const { |
| 128 | if (stick >= kJoystickPorts) { |
| 129 | WPI_LIB_FATAL(BadJoystickIndex); |
| 130 | return 0; |
| 131 | } |
| 132 | return m_joystickButtons[stick].buttons; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * The state of one joystick button. Button indexes begin at 1. |
| 137 | * |
| 138 | * @param stick The joystick to read. |
| 139 | * @param button The button index, beginning at 1. |
| 140 | * @return The state of the joystick button. |
| 141 | */ |
| 142 | bool DriverStation::GetStickButton(int stick, int button) { |
| 143 | if (stick >= kJoystickPorts) { |
| 144 | WPI_LIB_FATAL(BadJoystickIndex); |
| 145 | return false; |
| 146 | } |
| 147 | if (button == 0) { |
| 148 | return false; |
| 149 | } |
| 150 | if (button > m_joystickButtons[stick].count) { |
| 151 | return false; |
| 152 | } |
| 153 | |
| 154 | return ((0x1 << (button - 1)) & m_joystickButtons[stick].buttons) != 0; |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * Returns the number of axes on a given joystick port. |
| 159 | * |
| 160 | * @param stick The joystick port number |
| 161 | * @return The number of axes on the indicated joystick |
| 162 | */ |
| 163 | int DriverStation::GetStickAxisCount(int stick) const { |
| 164 | if (stick >= kJoystickPorts) { |
| 165 | WPI_LIB_FATAL(BadJoystickIndex); |
| 166 | return 0; |
| 167 | } |
| 168 | return m_joystickAxes[stick].count; |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Returns the number of POVs on a given joystick port. |
| 173 | * |
| 174 | * @param stick The joystick port number |
| 175 | * @return The number of POVs on the indicated joystick |
| 176 | */ |
| 177 | int DriverStation::GetStickPOVCount(int stick) const { |
| 178 | if (stick >= kJoystickPorts) { |
| 179 | WPI_LIB_FATAL(BadJoystickIndex); |
| 180 | return 0; |
| 181 | } |
| 182 | return m_joystickPOVs[stick].count; |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * Returns the number of buttons on a given joystick port. |
| 187 | * |
| 188 | * @param stick The joystick port number |
| 189 | * @return The number of buttons on the indicated joystick |
| 190 | */ |
| 191 | int DriverStation::GetStickButtonCount(int stick) const { |
| 192 | if (stick >= kJoystickPorts) { |
| 193 | WPI_LIB_FATAL(BadJoystickIndex); |
| 194 | return 0; |
| 195 | } |
| 196 | return m_joystickButtons[stick].count; |
| 197 | } |
| 198 | |
| 199 | /** |
| 200 | * Returns a boolean indicating if the controller is an xbox controller. |
| 201 | * |
| 202 | * @param stick The joystick port number |
| 203 | * @return A boolean that is true if the controller is an xbox controller. |
| 204 | */ |
| 205 | bool DriverStation::GetJoystickIsXbox(int stick) const { |
| 206 | if (stick >= kJoystickPorts) { |
| 207 | WPI_LIB_FATAL(BadJoystickIndex); |
| 208 | return false; |
| 209 | } |
| 210 | return static_cast<bool>(m_joystickDescriptor[stick].isXbox); |
| 211 | } |
| 212 | |
| 213 | /** |
| 214 | * Returns the type of joystick at a given port. |
| 215 | * |
| 216 | * @param stick The joystick port number |
| 217 | * @return The HID type of joystick at the given port |
| 218 | */ |
| 219 | int DriverStation::GetJoystickType(int stick) const { |
| 220 | if (stick >= kJoystickPorts) { |
| 221 | WPI_LIB_FATAL(BadJoystickIndex); |
| 222 | return -1; |
| 223 | } |
| 224 | return static_cast<int>(m_joystickDescriptor[stick].type); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Returns the name of the joystick at the given port. |
| 229 | * |
| 230 | * @param stick The joystick port number |
| 231 | * @return The name of the joystick at the given port |
| 232 | */ |
| 233 | std::string DriverStation::GetJoystickName(int stick) const { |
| 234 | if (stick >= kJoystickPorts) { |
| 235 | WPI_LIB_FATAL(BadJoystickIndex); |
| 236 | } |
| 237 | std::string retVal(m_joystickDescriptor[stick].name); |
| 238 | return retVal; |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Returns the types of Axes on a given joystick port. |
| 243 | * |
| 244 | * @param stick The joystick port number and the target axis |
| 245 | * @return What type of axis the axis is reporting to be |
| 246 | */ |
| 247 | int DriverStation::GetJoystickAxisType(int stick, int axis) const { |
| 248 | if (stick >= kJoystickPorts) { |
| 249 | WPI_LIB_FATAL(BadJoystickIndex); |
| 250 | return -1; |
| 251 | } |
| 252 | return m_joystickDescriptor[stick].axisTypes[axis]; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Check if the FPGA outputs are enabled. |
| 257 | * |
| 258 | * The outputs may be disabled if the robot is disabled or e-stopped, the |
| 259 | * watchdog has expired, or if the roboRIO browns out. |
| 260 | * |
| 261 | * @return True if the FPGA outputs are enabled. |
| 262 | */ |
| 263 | bool DriverStation::IsSysActive() const { |
| 264 | int32_t status = 0; |
| 265 | bool retVal = HAL_GetSystemActive(&status); |
| 266 | wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); |
| 267 | return retVal; |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Check if the system is browned out. |
| 272 | * |
| 273 | * @return True if the system is browned out |
| 274 | */ |
| 275 | bool DriverStation::IsBrownedOut() const { |
| 276 | int32_t status = 0; |
| 277 | bool retVal = HAL_GetBrownedOut(&status); |
| 278 | wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); |
| 279 | return retVal; |
| 280 | } |
| 281 | |
| 282 | /** |
| 283 | * Return the alliance that the driver station says it is on. |
| 284 | * |
| 285 | * This could return kRed or kBlue. |
| 286 | * |
| 287 | * @return The Alliance enum (kRed, kBlue or kInvalid) |
| 288 | */ |
| 289 | DriverStation::Alliance DriverStation::GetAlliance() const { |
| 290 | int32_t status = 0; |
| 291 | auto allianceStationID = HAL_GetAllianceStation(&status); |
| 292 | switch (allianceStationID) { |
| 293 | case HAL_AllianceStationID_kRed1: |
| 294 | case HAL_AllianceStationID_kRed2: |
| 295 | case HAL_AllianceStationID_kRed3: |
| 296 | return kRed; |
| 297 | case HAL_AllianceStationID_kBlue1: |
| 298 | case HAL_AllianceStationID_kBlue2: |
| 299 | case HAL_AllianceStationID_kBlue3: |
| 300 | return kBlue; |
| 301 | default: |
| 302 | return kInvalid; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Return the driver station location on the field. |
| 308 | * |
| 309 | * This could return 1, 2, or 3. |
| 310 | * |
| 311 | * @return The location of the driver station (1-3, 0 for invalid) |
| 312 | */ |
| 313 | int DriverStation::GetLocation() const { |
| 314 | int32_t status = 0; |
| 315 | auto allianceStationID = HAL_GetAllianceStation(&status); |
| 316 | switch (allianceStationID) { |
| 317 | case HAL_AllianceStationID_kRed1: |
| 318 | case HAL_AllianceStationID_kBlue1: |
| 319 | return 1; |
| 320 | case HAL_AllianceStationID_kRed2: |
| 321 | case HAL_AllianceStationID_kBlue2: |
| 322 | return 2; |
| 323 | case HAL_AllianceStationID_kRed3: |
| 324 | case HAL_AllianceStationID_kBlue3: |
| 325 | return 3; |
| 326 | default: |
| 327 | return 0; |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * Return the approximate match time. |
| 333 | * |
| 334 | * The FMS does not send an official match time to the robots, but does send an |
| 335 | * approximate match time. The value will count down the time remaining in the |
| 336 | * current period (auto or teleop). |
| 337 | * |
| 338 | * Warning: This is not an official time (so it cannot be used to dispute ref |
| 339 | * calls or guarantee that a function will trigger before the match ends). |
| 340 | * |
| 341 | * The Practice Match function of the DS approximates the behaviour seen on the |
| 342 | * field. |
| 343 | * |
| 344 | * @return Time remaining in current match period (auto or teleop) |
| 345 | */ |
| 346 | double DriverStation::GetMatchTime() const { |
| 347 | int32_t status; |
| 348 | return HAL_GetMatchTime(&status); |
| 349 | } |
| 350 | |
| 351 | /** |
| 352 | * Read the battery voltage. |
| 353 | * |
| 354 | * @return The battery voltage in Volts. |
| 355 | */ |
| 356 | double DriverStation::GetBatteryVoltage() const { |
| 357 | int32_t status = 0; |
| 358 | double voltage = HAL_GetVinVoltage(&status); |
| 359 | wpi_setErrorWithContext(status, "getVinVoltage"); |
| 360 | |
| 361 | return voltage; |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * Copy data from the DS task for the user. |
| 366 | * |
| 367 | * If no new data exists, it will just be returned, otherwise |
| 368 | * the data will be copied from the DS polling loop. |
| 369 | */ |
| 370 | void DriverStation::GetData() { |
| 371 | // Get the status of all of the joysticks, and save to the cache |
| 372 | for (uint8_t stick = 0; stick < kJoystickPorts; stick++) { |
| 373 | HAL_GetJoystickAxes(stick, &m_joystickAxesCache[stick]); |
| 374 | HAL_GetJoystickPOVs(stick, &m_joystickPOVsCache[stick]); |
| 375 | HAL_GetJoystickButtons(stick, &m_joystickButtonsCache[stick]); |
| 376 | HAL_GetJoystickDescriptor(stick, &m_joystickDescriptorCache[stick]); |
| 377 | } |
| 378 | m_joystickAxes.swap(m_joystickAxesCache); |
| 379 | m_joystickPOVs.swap(m_joystickPOVsCache); |
| 380 | m_joystickButtons.swap(m_joystickButtonsCache); |
| 381 | m_joystickDescriptor.swap(m_joystickDescriptorCache); |
| 382 | |
| 383 | HAL_ControlWord control_word; |
| 384 | HAL_GetControlWord(&control_word); |
| 385 | is_enabled_ = control_word.enabled; |
| 386 | is_autonomous_ = control_word.autonomous; |
| 387 | is_test_mode_ = control_word.test; |
| 388 | is_fms_attached_ = control_word.fmsAttached; |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * DriverStation constructor. |
| 393 | * |
| 394 | * This is only called once the first time GetInstance() is called |
| 395 | */ |
| 396 | DriverStation::DriverStation() { |
| 397 | // The HAL_Observe* functions let the Driver Station know that the |
| 398 | // robot code is alive (this influences the Robot Code status light on the |
| 399 | // DS, and if the DS thinks you don't have robot code, then you can't enable). |
| 400 | HAL_ObserveUserProgramStarting(); |
| 401 | |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 402 | m_joystickAxes = aos::make_unique<HAL_JoystickAxes[]>(kJoystickPorts); |
| 403 | m_joystickPOVs = aos::make_unique<HAL_JoystickPOVs[]>(kJoystickPorts); |
| 404 | m_joystickButtons = aos::make_unique<HAL_JoystickButtons[]>(kJoystickPorts); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 405 | m_joystickDescriptor = |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 406 | aos::make_unique<HAL_JoystickDescriptor[]>(kJoystickPorts); |
| 407 | m_joystickAxesCache = aos::make_unique<HAL_JoystickAxes[]>(kJoystickPorts); |
| 408 | m_joystickPOVsCache = aos::make_unique<HAL_JoystickPOVs[]>(kJoystickPorts); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 409 | m_joystickButtonsCache = |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 410 | aos::make_unique<HAL_JoystickButtons[]>(kJoystickPorts); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 411 | m_joystickDescriptorCache = |
Austin Schuh | f6b9463 | 2019-02-02 22:11:27 -0800 | [diff] [blame] | 412 | aos::make_unique<HAL_JoystickDescriptor[]>(kJoystickPorts); |
Parker Schuh | d3b7a887 | 2018-02-19 16:42:27 -0800 | [diff] [blame] | 413 | |
| 414 | // All joysticks should default to having zero axes, povs and buttons, so |
| 415 | // uninitialized memory doesn't get sent to speed controllers. |
| 416 | for (unsigned int i = 0; i < kJoystickPorts; i++) { |
| 417 | m_joystickAxes[i].count = 0; |
| 418 | m_joystickPOVs[i].count = 0; |
| 419 | m_joystickButtons[i].count = 0; |
| 420 | m_joystickDescriptor[i].isXbox = 0; |
| 421 | m_joystickDescriptor[i].type = -1; |
| 422 | m_joystickDescriptor[i].name[0] = '\0'; |
| 423 | |
| 424 | m_joystickAxesCache[i].count = 0; |
| 425 | m_joystickPOVsCache[i].count = 0; |
| 426 | m_joystickButtonsCache[i].count = 0; |
| 427 | m_joystickDescriptorCache[i].isXbox = 0; |
| 428 | m_joystickDescriptorCache[i].type = -1; |
| 429 | m_joystickDescriptorCache[i].name[0] = '\0'; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | void DriverStation::RunIteration(std::function<void()> on_data) { |
| 434 | HAL_WaitForDSData(); |
| 435 | GetData(); |
| 436 | |
| 437 | // We have to feed some sort of watchdog so that the driver's station knows |
| 438 | // that the robot code is still alive. HAL_ObserveUserProgramStarting must be |
| 439 | // called during initialization (currently called in the constructor of this |
| 440 | // class). |
| 441 | if (!IsEnabled()) { |
| 442 | HAL_ObserveUserProgramDisabled(); |
| 443 | } else if (IsAutonomous()) { |
| 444 | HAL_ObserveUserProgramAutonomous(); |
| 445 | } else if (IsTestMode()) { |
| 446 | HAL_ObserveUserProgramTest(); |
| 447 | } else { |
| 448 | HAL_ObserveUserProgramTeleop(); |
| 449 | } |
| 450 | |
| 451 | on_data(); |
| 452 | } |