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.wpilibj; 006 007import edu.wpi.first.hal.AllianceStationID; 008import edu.wpi.first.hal.ControlWord; 009import edu.wpi.first.hal.HAL; 010import edu.wpi.first.hal.MatchInfoData; 011import edu.wpi.first.networktables.NetworkTable; 012import edu.wpi.first.networktables.NetworkTableEntry; 013import edu.wpi.first.networktables.NetworkTableInstance; 014import java.nio.ByteBuffer; 015import java.util.concurrent.TimeUnit; 016import java.util.concurrent.locks.Condition; 017import java.util.concurrent.locks.Lock; 018import java.util.concurrent.locks.ReentrantLock; 019 020/** Provide access to the network communication data to / from the Driver Station. */ 021public class DriverStation { 022 /** Number of Joystick Ports. */ 023 public static final int kJoystickPorts = 6; 024 025 private static class HALJoystickButtons { 026 public int m_buttons; 027 public byte m_count; 028 } 029 030 private static class HALJoystickAxes { 031 public float[] m_axes; 032 public short m_count; 033 034 HALJoystickAxes(int count) { 035 m_axes = new float[count]; 036 } 037 } 038 039 private static class HALJoystickPOVs { 040 public short[] m_povs; 041 public short m_count; 042 043 HALJoystickPOVs(int count) { 044 m_povs = new short[count]; 045 for (int i = 0; i < count; i++) { 046 m_povs[i] = -1; 047 } 048 } 049 } 050 051 /** The robot alliance that the robot is a part of. */ 052 public enum Alliance { 053 Red, 054 Blue, 055 Invalid 056 } 057 058 public enum MatchType { 059 None, 060 Practice, 061 Qualification, 062 Elimination 063 } 064 065 private static final double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0; 066 private static double m_nextMessageTime; 067 068 private static class DriverStationTask implements Runnable { 069 DriverStationTask() {} 070 071 @Override 072 public void run() { 073 DriverStation.run(); 074 } 075 } /* DriverStationTask */ 076 077 private static class MatchDataSender { 078 @SuppressWarnings("MemberName") 079 NetworkTable table; 080 081 @SuppressWarnings("MemberName") 082 NetworkTableEntry typeMetadata; 083 084 @SuppressWarnings("MemberName") 085 NetworkTableEntry gameSpecificMessage; 086 087 @SuppressWarnings("MemberName") 088 NetworkTableEntry eventName; 089 090 @SuppressWarnings("MemberName") 091 NetworkTableEntry matchNumber; 092 093 @SuppressWarnings("MemberName") 094 NetworkTableEntry replayNumber; 095 096 @SuppressWarnings("MemberName") 097 NetworkTableEntry matchType; 098 099 @SuppressWarnings("MemberName") 100 NetworkTableEntry alliance; 101 102 @SuppressWarnings("MemberName") 103 NetworkTableEntry station; 104 105 @SuppressWarnings("MemberName") 106 NetworkTableEntry controlWord; 107 108 @SuppressWarnings("MemberName") 109 boolean oldIsRedAlliance = true; 110 111 @SuppressWarnings("MemberName") 112 int oldStationNumber = 1; 113 114 @SuppressWarnings("MemberName") 115 String oldEventName = ""; 116 117 @SuppressWarnings("MemberName") 118 String oldGameSpecificMessage = ""; 119 120 @SuppressWarnings("MemberName") 121 int oldMatchNumber; 122 123 @SuppressWarnings("MemberName") 124 int oldReplayNumber; 125 126 @SuppressWarnings("MemberName") 127 int oldMatchType; 128 129 @SuppressWarnings("MemberName") 130 int oldControlWord; 131 132 MatchDataSender() { 133 table = NetworkTableInstance.getDefault().getTable("FMSInfo"); 134 typeMetadata = table.getEntry(".type"); 135 typeMetadata.forceSetString("FMSInfo"); 136 gameSpecificMessage = table.getEntry("GameSpecificMessage"); 137 gameSpecificMessage.forceSetString(""); 138 eventName = table.getEntry("EventName"); 139 eventName.forceSetString(""); 140 matchNumber = table.getEntry("MatchNumber"); 141 matchNumber.forceSetDouble(0); 142 replayNumber = table.getEntry("ReplayNumber"); 143 replayNumber.forceSetDouble(0); 144 matchType = table.getEntry("MatchType"); 145 matchType.forceSetDouble(0); 146 alliance = table.getEntry("IsRedAlliance"); 147 alliance.forceSetBoolean(true); 148 station = table.getEntry("StationNumber"); 149 station.forceSetDouble(1); 150 controlWord = table.getEntry("FMSControlData"); 151 controlWord.forceSetDouble(0); 152 } 153 154 private void sendMatchData() { 155 AllianceStationID allianceID = HAL.getAllianceStation(); 156 boolean isRedAlliance = false; 157 int stationNumber = 1; 158 switch (allianceID) { 159 case Blue1: 160 isRedAlliance = false; 161 stationNumber = 1; 162 break; 163 case Blue2: 164 isRedAlliance = false; 165 stationNumber = 2; 166 break; 167 case Blue3: 168 isRedAlliance = false; 169 stationNumber = 3; 170 break; 171 case Red1: 172 isRedAlliance = true; 173 stationNumber = 1; 174 break; 175 case Red2: 176 isRedAlliance = true; 177 stationNumber = 2; 178 break; 179 default: 180 isRedAlliance = true; 181 stationNumber = 3; 182 break; 183 } 184 185 String currentEventName; 186 String currentGameSpecificMessage; 187 int currentMatchNumber; 188 int currentReplayNumber; 189 int currentMatchType; 190 int currentControlWord; 191 m_cacheDataMutex.lock(); 192 try { 193 currentEventName = DriverStation.m_matchInfo.eventName; 194 currentGameSpecificMessage = DriverStation.m_matchInfo.gameSpecificMessage; 195 currentMatchNumber = DriverStation.m_matchInfo.matchNumber; 196 currentReplayNumber = DriverStation.m_matchInfo.replayNumber; 197 currentMatchType = DriverStation.m_matchInfo.matchType; 198 } finally { 199 m_cacheDataMutex.unlock(); 200 } 201 currentControlWord = HAL.nativeGetControlWord(); 202 203 if (oldIsRedAlliance != isRedAlliance) { 204 alliance.setBoolean(isRedAlliance); 205 oldIsRedAlliance = isRedAlliance; 206 } 207 if (oldStationNumber != stationNumber) { 208 station.setDouble(stationNumber); 209 oldStationNumber = stationNumber; 210 } 211 if (!oldEventName.equals(currentEventName)) { 212 eventName.setString(currentEventName); 213 oldEventName = currentEventName; 214 } 215 if (!oldGameSpecificMessage.equals(currentGameSpecificMessage)) { 216 gameSpecificMessage.setString(currentGameSpecificMessage); 217 oldGameSpecificMessage = currentGameSpecificMessage; 218 } 219 if (currentMatchNumber != oldMatchNumber) { 220 matchNumber.setDouble(currentMatchNumber); 221 oldMatchNumber = currentMatchNumber; 222 } 223 if (currentReplayNumber != oldReplayNumber) { 224 replayNumber.setDouble(currentReplayNumber); 225 oldReplayNumber = currentReplayNumber; 226 } 227 if (currentMatchType != oldMatchType) { 228 matchType.setDouble(currentMatchType); 229 oldMatchType = currentMatchType; 230 } 231 if (currentControlWord != oldControlWord) { 232 controlWord.setDouble(currentControlWord); 233 oldControlWord = currentControlWord; 234 } 235 } 236 } 237 238 private static DriverStation instance = new DriverStation(); 239 240 // Joystick User Data 241 private static HALJoystickAxes[] m_joystickAxes = new HALJoystickAxes[kJoystickPorts]; 242 private static HALJoystickPOVs[] m_joystickPOVs = new HALJoystickPOVs[kJoystickPorts]; 243 private static HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[kJoystickPorts]; 244 private static MatchInfoData m_matchInfo = new MatchInfoData(); 245 246 // Joystick Cached Data 247 private static HALJoystickAxes[] m_joystickAxesCache = new HALJoystickAxes[kJoystickPorts]; 248 private static HALJoystickPOVs[] m_joystickPOVsCache = new HALJoystickPOVs[kJoystickPorts]; 249 private static HALJoystickButtons[] m_joystickButtonsCache = 250 new HALJoystickButtons[kJoystickPorts]; 251 private static MatchInfoData m_matchInfoCache = new MatchInfoData(); 252 253 // Joystick button rising/falling edge flags 254 private static int[] m_joystickButtonsPressed = new int[kJoystickPorts]; 255 private static int[] m_joystickButtonsReleased = new int[kJoystickPorts]; 256 257 // preallocated byte buffer for button count 258 private static final ByteBuffer m_buttonCountBuffer = ByteBuffer.allocateDirect(1); 259 260 private static final MatchDataSender m_matchDataSender; 261 262 // Internal Driver Station thread 263 private static Thread m_thread; 264 265 private static volatile boolean m_threadKeepAlive = true; 266 267 private static final ReentrantLock m_cacheDataMutex = new ReentrantLock(); 268 269 private static final Lock m_waitForDataMutex; 270 private static final Condition m_waitForDataCond; 271 private static int m_waitForDataCount; 272 private static final ThreadLocal<Integer> m_lastCount = ThreadLocal.withInitial(() -> 0); 273 274 private static boolean m_silenceJoystickWarning; 275 276 // Robot state status variables 277 private static boolean m_userInDisabled; 278 private static boolean m_userInAutonomous; 279 private static boolean m_userInTeleop; 280 private static boolean m_userInTest; 281 282 // Control word variables 283 private static final Object m_controlWordMutex; 284 private static final ControlWord m_controlWordCache; 285 private static long m_lastControlWordUpdate; 286 287 /** 288 * Gets an instance of the DriverStation. 289 * 290 * @return The DriverStation. 291 * @deprecated Use the static methods 292 */ 293 @Deprecated 294 public static DriverStation getInstance() { 295 return DriverStation.instance; 296 } 297 298 /** 299 * DriverStation constructor. 300 * 301 * <p>The single DriverStation instance is created statically with the instance static member 302 * variable. 303 */ 304 private DriverStation() {} 305 306 static { 307 HAL.initialize(500, 0); 308 m_waitForDataCount = 0; 309 m_waitForDataMutex = new ReentrantLock(); 310 m_waitForDataCond = m_waitForDataMutex.newCondition(); 311 312 for (int i = 0; i < kJoystickPorts; i++) { 313 m_joystickButtons[i] = new HALJoystickButtons(); 314 m_joystickAxes[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes); 315 m_joystickPOVs[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs); 316 317 m_joystickButtonsCache[i] = new HALJoystickButtons(); 318 m_joystickAxesCache[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes); 319 m_joystickPOVsCache[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs); 320 } 321 322 m_controlWordMutex = new Object(); 323 m_controlWordCache = new ControlWord(); 324 m_lastControlWordUpdate = 0; 325 326 m_matchDataSender = new MatchDataSender(); 327 328 m_thread = new Thread(new DriverStationTask(), "FRCDriverStation"); 329 m_thread.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2); 330 331 m_thread.start(); 332 } 333 334 /** Kill the thread. */ 335 public static synchronized void release() { 336 m_threadKeepAlive = false; 337 if (m_thread != null) { 338 try { 339 m_thread.join(); 340 } catch (InterruptedException ex) { 341 Thread.currentThread().interrupt(); 342 } 343 m_thread = null; 344 } 345 } 346 347 /** 348 * Report error to Driver Station. Optionally appends Stack trace to error message. 349 * 350 * @param error The error to report. 351 * @param printTrace If true, append stack trace to error string 352 */ 353 public static void reportError(String error, boolean printTrace) { 354 reportErrorImpl(true, 1, error, printTrace); 355 } 356 357 /** 358 * Report error to Driver Station. Appends provided stack trace to error message. 359 * 360 * @param error The error to report. 361 * @param stackTrace The stack trace to append 362 */ 363 public static void reportError(String error, StackTraceElement[] stackTrace) { 364 reportErrorImpl(true, 1, error, stackTrace); 365 } 366 367 /** 368 * Report warning to Driver Station. Optionally appends Stack trace to warning message. 369 * 370 * @param warning The warning to report. 371 * @param printTrace If true, append stack trace to warning string 372 */ 373 public static void reportWarning(String warning, boolean printTrace) { 374 reportErrorImpl(false, 1, warning, printTrace); 375 } 376 377 /** 378 * Report warning to Driver Station. Appends provided stack trace to warning message. 379 * 380 * @param warning The warning to report. 381 * @param stackTrace The stack trace to append 382 */ 383 public static void reportWarning(String warning, StackTraceElement[] stackTrace) { 384 reportErrorImpl(false, 1, warning, stackTrace); 385 } 386 387 private static void reportErrorImpl(boolean isError, int code, String error, boolean printTrace) { 388 reportErrorImpl(isError, code, error, printTrace, Thread.currentThread().getStackTrace(), 3); 389 } 390 391 private static void reportErrorImpl( 392 boolean isError, int code, String error, StackTraceElement[] stackTrace) { 393 reportErrorImpl(isError, code, error, true, stackTrace, 0); 394 } 395 396 private static void reportErrorImpl( 397 boolean isError, 398 int code, 399 String error, 400 boolean printTrace, 401 StackTraceElement[] stackTrace, 402 int stackTraceFirst) { 403 String locString; 404 if (stackTrace.length >= stackTraceFirst + 1) { 405 locString = stackTrace[stackTraceFirst].toString(); 406 } else { 407 locString = ""; 408 } 409 StringBuilder traceString = new StringBuilder(); 410 if (printTrace) { 411 boolean haveLoc = false; 412 for (int i = stackTraceFirst; i < stackTrace.length; i++) { 413 String loc = stackTrace[i].toString(); 414 traceString.append("\tat ").append(loc).append('\n'); 415 // get first user function 416 if (!haveLoc && !loc.startsWith("edu.wpi.first")) { 417 locString = loc; 418 haveLoc = true; 419 } 420 } 421 } 422 HAL.sendError(isError, code, false, error, locString, traceString.toString(), true); 423 } 424 425 /** 426 * The state of one joystick button. Button indexes begin at 1. 427 * 428 * @param stick The joystick to read. 429 * @param button The button index, beginning at 1. 430 * @return The state of the joystick button. 431 */ 432 public static boolean getStickButton(final int stick, final int button) { 433 if (stick < 0 || stick >= kJoystickPorts) { 434 throw new IllegalArgumentException("Joystick index is out of range, should be 0-3"); 435 } 436 if (button <= 0) { 437 reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n"); 438 return false; 439 } 440 441 m_cacheDataMutex.lock(); 442 try { 443 if (button <= m_joystickButtons[stick].m_count) { 444 return (m_joystickButtons[stick].m_buttons & 1 << (button - 1)) != 0; 445 } 446 } finally { 447 m_cacheDataMutex.unlock(); 448 } 449 450 reportJoystickUnpluggedWarning( 451 "Joystick Button " 452 + button 453 + " on port " 454 + stick 455 + " not available, check if controller is plugged in"); 456 return false; 457 } 458 459 /** 460 * Whether one joystick button was pressed since the last check. Button indexes begin at 1. 461 * 462 * @param stick The joystick to read. 463 * @param button The button index, beginning at 1. 464 * @return Whether the joystick button was pressed since the last check. 465 */ 466 public static boolean getStickButtonPressed(final int stick, final int button) { 467 if (button <= 0) { 468 reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n"); 469 return false; 470 } 471 if (stick < 0 || stick >= kJoystickPorts) { 472 throw new IllegalArgumentException("Joystick index is out of range, should be 0-3"); 473 } 474 475 m_cacheDataMutex.lock(); 476 try { 477 if (button <= m_joystickButtons[stick].m_count) { 478 // If button was pressed, clear flag and return true 479 if ((m_joystickButtonsPressed[stick] & 1 << (button - 1)) != 0) { 480 m_joystickButtonsPressed[stick] &= ~(1 << (button - 1)); 481 return true; 482 } else { 483 return false; 484 } 485 } 486 } finally { 487 m_cacheDataMutex.unlock(); 488 } 489 490 reportJoystickUnpluggedWarning( 491 "Joystick Button " 492 + button 493 + " on port " 494 + stick 495 + " not available, check if controller is plugged in"); 496 return false; 497 } 498 499 /** 500 * Whether one joystick button was released since the last check. Button indexes begin at 1. 501 * 502 * @param stick The joystick to read. 503 * @param button The button index, beginning at 1. 504 * @return Whether the joystick button was released since the last check. 505 */ 506 public static boolean getStickButtonReleased(final int stick, final int button) { 507 if (button <= 0) { 508 reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n"); 509 return false; 510 } 511 if (stick < 0 || stick >= kJoystickPorts) { 512 throw new IllegalArgumentException("Joystick index is out of range, should be 0-3"); 513 } 514 515 m_cacheDataMutex.lock(); 516 try { 517 if (button <= m_joystickButtons[stick].m_count) { 518 // If button was released, clear flag and return true 519 if ((m_joystickButtonsReleased[stick] & 1 << (button - 1)) != 0) { 520 m_joystickButtonsReleased[stick] &= ~(1 << (button - 1)); 521 return true; 522 } else { 523 return false; 524 } 525 } 526 } finally { 527 m_cacheDataMutex.unlock(); 528 } 529 530 reportJoystickUnpluggedWarning( 531 "Joystick Button " 532 + button 533 + " on port " 534 + stick 535 + " not available, check if controller is plugged in"); 536 return false; 537 } 538 539 /** 540 * Get the value of the axis on a joystick. This depends on the mapping of the joystick connected 541 * to the specified port. 542 * 543 * @param stick The joystick to read. 544 * @param axis The analog axis value to read from the joystick. 545 * @return The value of the axis on the joystick. 546 */ 547 public static double getStickAxis(int stick, int axis) { 548 if (stick < 0 || stick >= kJoystickPorts) { 549 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 550 } 551 if (axis < 0 || axis >= HAL.kMaxJoystickAxes) { 552 throw new IllegalArgumentException("Joystick axis is out of range"); 553 } 554 555 m_cacheDataMutex.lock(); 556 try { 557 if (axis < m_joystickAxes[stick].m_count) { 558 return m_joystickAxes[stick].m_axes[axis]; 559 } 560 } finally { 561 m_cacheDataMutex.unlock(); 562 } 563 564 reportJoystickUnpluggedWarning( 565 "Joystick axis " 566 + axis 567 + " on port " 568 + stick 569 + " not available, check if controller is plugged in"); 570 return 0.0; 571 } 572 573 /** 574 * Get the state of a POV on the joystick. 575 * 576 * @param stick The joystick to read. 577 * @param pov The POV to read. 578 * @return the angle of the POV in degrees, or -1 if the POV is not pressed. 579 */ 580 public static int getStickPOV(int stick, int pov) { 581 if (stick < 0 || stick >= kJoystickPorts) { 582 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 583 } 584 if (pov < 0 || pov >= HAL.kMaxJoystickPOVs) { 585 throw new IllegalArgumentException("Joystick POV is out of range"); 586 } 587 588 m_cacheDataMutex.lock(); 589 try { 590 if (pov < m_joystickPOVs[stick].m_count) { 591 return m_joystickPOVs[stick].m_povs[pov]; 592 } 593 } finally { 594 m_cacheDataMutex.unlock(); 595 } 596 597 reportJoystickUnpluggedWarning( 598 "Joystick POV " 599 + pov 600 + " on port " 601 + stick 602 + " not available, check if controller is plugged in"); 603 return -1; 604 } 605 606 /** 607 * The state of the buttons on the joystick. 608 * 609 * @param stick The joystick to read. 610 * @return The state of the buttons on the joystick. 611 */ 612 public static int getStickButtons(final int stick) { 613 if (stick < 0 || stick >= kJoystickPorts) { 614 throw new IllegalArgumentException("Joystick index is out of range, should be 0-3"); 615 } 616 617 m_cacheDataMutex.lock(); 618 try { 619 return m_joystickButtons[stick].m_buttons; 620 } finally { 621 m_cacheDataMutex.unlock(); 622 } 623 } 624 625 /** 626 * Returns the number of axes on a given joystick port. 627 * 628 * @param stick The joystick port number 629 * @return The number of axes on the indicated joystick 630 */ 631 public static int getStickAxisCount(int stick) { 632 if (stick < 0 || stick >= kJoystickPorts) { 633 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 634 } 635 636 m_cacheDataMutex.lock(); 637 try { 638 return m_joystickAxes[stick].m_count; 639 } finally { 640 m_cacheDataMutex.unlock(); 641 } 642 } 643 644 /** 645 * Returns the number of POVs on a given joystick port. 646 * 647 * @param stick The joystick port number 648 * @return The number of POVs on the indicated joystick 649 */ 650 public static int getStickPOVCount(int stick) { 651 if (stick < 0 || stick >= kJoystickPorts) { 652 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 653 } 654 655 m_cacheDataMutex.lock(); 656 try { 657 return m_joystickPOVs[stick].m_count; 658 } finally { 659 m_cacheDataMutex.unlock(); 660 } 661 } 662 663 /** 664 * Gets the number of buttons on a joystick. 665 * 666 * @param stick The joystick port number 667 * @return The number of buttons on the indicated joystick 668 */ 669 public static int getStickButtonCount(int stick) { 670 if (stick < 0 || stick >= kJoystickPorts) { 671 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 672 } 673 674 m_cacheDataMutex.lock(); 675 try { 676 return m_joystickButtons[stick].m_count; 677 } finally { 678 m_cacheDataMutex.unlock(); 679 } 680 } 681 682 /** 683 * Gets the value of isXbox on a joystick. 684 * 685 * @param stick The joystick port number 686 * @return A boolean that returns the value of isXbox 687 */ 688 public static boolean getJoystickIsXbox(int stick) { 689 if (stick < 0 || stick >= kJoystickPorts) { 690 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 691 } 692 693 return HAL.getJoystickIsXbox((byte) stick) == 1; 694 } 695 696 /** 697 * Gets the value of type on a joystick. 698 * 699 * @param stick The joystick port number 700 * @return The value of type 701 */ 702 public static int getJoystickType(int stick) { 703 if (stick < 0 || stick >= kJoystickPorts) { 704 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 705 } 706 707 return HAL.getJoystickType((byte) stick); 708 } 709 710 /** 711 * Gets the name of the joystick at a port. 712 * 713 * @param stick The joystick port number 714 * @return The value of name 715 */ 716 public static String getJoystickName(int stick) { 717 if (stick < 0 || stick >= kJoystickPorts) { 718 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 719 } 720 721 return HAL.getJoystickName((byte) stick); 722 } 723 724 /** 725 * Returns the types of Axes on a given joystick port. 726 * 727 * @param stick The joystick port number 728 * @param axis The target axis 729 * @return What type of axis the axis is reporting to be 730 */ 731 public static int getJoystickAxisType(int stick, int axis) { 732 if (stick < 0 || stick >= kJoystickPorts) { 733 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5"); 734 } 735 736 return HAL.getJoystickAxisType((byte) stick, (byte) axis); 737 } 738 739 /** 740 * Returns if a joystick is connected to the Driver Station. 741 * 742 * <p>This makes a best effort guess by looking at the reported number of axis, buttons, and POVs 743 * attached. 744 * 745 * @param stick The joystick port number 746 * @return true if a joystick is connected 747 */ 748 public static boolean isJoystickConnected(int stick) { 749 return getStickAxisCount(stick) > 0 750 || getStickButtonCount(stick) > 0 751 || getStickPOVCount(stick) > 0; 752 } 753 754 /** 755 * Gets a value indicating whether the Driver Station requires the robot to be enabled. 756 * 757 * @return True if the robot is enabled, false otherwise. 758 */ 759 public static boolean isEnabled() { 760 synchronized (m_controlWordMutex) { 761 updateControlWord(false); 762 return m_controlWordCache.getEnabled() && m_controlWordCache.getDSAttached(); 763 } 764 } 765 766 /** 767 * Gets a value indicating whether the Driver Station requires the robot to be disabled. 768 * 769 * @return True if the robot should be disabled, false otherwise. 770 */ 771 public static boolean isDisabled() { 772 return !isEnabled(); 773 } 774 775 /** 776 * Gets a value indicating whether the Robot is e-stopped. 777 * 778 * @return True if the robot is e-stopped, false otherwise. 779 */ 780 public static boolean isEStopped() { 781 synchronized (m_controlWordMutex) { 782 updateControlWord(false); 783 return m_controlWordCache.getEStop(); 784 } 785 } 786 787 /** 788 * Gets a value indicating whether the Driver Station requires the robot to be running in 789 * autonomous mode. 790 * 791 * @return True if autonomous mode should be enabled, false otherwise. 792 */ 793 public static boolean isAutonomous() { 794 synchronized (m_controlWordMutex) { 795 updateControlWord(false); 796 return m_controlWordCache.getAutonomous(); 797 } 798 } 799 800 /** 801 * Gets a value indicating whether the Driver Station requires the robot to be running in 802 * autonomous mode and enabled. 803 * 804 * @return True if autonomous should be set and the robot should be enabled. 805 */ 806 public static boolean isAutonomousEnabled() { 807 synchronized (m_controlWordMutex) { 808 updateControlWord(false); 809 return m_controlWordCache.getAutonomous() && m_controlWordCache.getEnabled(); 810 } 811 } 812 813 /** 814 * Gets a value indicating whether the Driver Station requires the robot to be running in 815 * operator-controlled mode. 816 * 817 * @return True if operator-controlled mode should be enabled, false otherwise. 818 * @deprecated Use isTeleop() instead. 819 */ 820 @Deprecated(since = "2022", forRemoval = true) 821 public static boolean isOperatorControl() { 822 return isTeleop(); 823 } 824 825 /** 826 * Gets a value indicating whether the Driver Station requires the robot to be running in 827 * operator-controlled mode. 828 * 829 * @return True if operator-controlled mode should be enabled, false otherwise. 830 */ 831 public static boolean isTeleop() { 832 return !(isAutonomous() || isTest()); 833 } 834 835 /** 836 * Gets a value indicating whether the Driver Station requires the robot to be running in 837 * operator-controller mode and enabled. 838 * 839 * @return True if operator-controlled mode should be set and the robot should be enabled. 840 * @deprecated Use isTeleopEnabled() instead. 841 */ 842 @Deprecated(since = "2022", forRemoval = true) 843 public static boolean isOperatorControlEnabled() { 844 return isTeleopEnabled(); 845 } 846 847 /** 848 * Gets a value indicating whether the Driver Station requires the robot to be running in 849 * operator-controller mode and enabled. 850 * 851 * @return True if operator-controlled mode should be set and the robot should be enabled. 852 */ 853 public static boolean isTeleopEnabled() { 854 synchronized (m_controlWordMutex) { 855 updateControlWord(false); 856 return !m_controlWordCache.getAutonomous() 857 && !m_controlWordCache.getTest() 858 && m_controlWordCache.getEnabled(); 859 } 860 } 861 862 /** 863 * Gets a value indicating whether the Driver Station requires the robot to be running in test 864 * mode. 865 * 866 * @return True if test mode should be enabled, false otherwise. 867 */ 868 public static boolean isTest() { 869 synchronized (m_controlWordMutex) { 870 updateControlWord(false); 871 return m_controlWordCache.getTest(); 872 } 873 } 874 875 /** 876 * Gets a value indicating whether the Driver Station is attached. 877 * 878 * @return True if Driver Station is attached, false otherwise. 879 */ 880 public static boolean isDSAttached() { 881 synchronized (m_controlWordMutex) { 882 updateControlWord(false); 883 return m_controlWordCache.getDSAttached(); 884 } 885 } 886 887 /** 888 * Gets if a new control packet from the driver station arrived since the last time this function 889 * was called. 890 * 891 * @return True if the control data has been updated since the last call. 892 */ 893 public static boolean isNewControlData() { 894 m_waitForDataMutex.lock(); 895 try { 896 int currentCount = m_waitForDataCount; 897 if (m_lastCount.get() != currentCount) { 898 m_lastCount.set(currentCount); 899 return true; 900 } 901 } finally { 902 m_waitForDataMutex.unlock(); 903 } 904 return false; 905 } 906 907 /** 908 * Gets if the driver station attached to a Field Management System. 909 * 910 * @return true if the robot is competing on a field being controlled by a Field Management System 911 */ 912 public static boolean isFMSAttached() { 913 synchronized (m_controlWordMutex) { 914 updateControlWord(false); 915 return m_controlWordCache.getFMSAttached(); 916 } 917 } 918 919 /** 920 * Get the game specific message. 921 * 922 * @return the game specific message 923 */ 924 public static String getGameSpecificMessage() { 925 m_cacheDataMutex.lock(); 926 try { 927 return m_matchInfo.gameSpecificMessage; 928 } finally { 929 m_cacheDataMutex.unlock(); 930 } 931 } 932 933 /** 934 * Get the event name. 935 * 936 * @return the event name 937 */ 938 public static String getEventName() { 939 m_cacheDataMutex.lock(); 940 try { 941 return m_matchInfo.eventName; 942 } finally { 943 m_cacheDataMutex.unlock(); 944 } 945 } 946 947 /** 948 * Get the match type. 949 * 950 * @return the match type 951 */ 952 public static MatchType getMatchType() { 953 int matchType; 954 m_cacheDataMutex.lock(); 955 try { 956 matchType = m_matchInfo.matchType; 957 } finally { 958 m_cacheDataMutex.unlock(); 959 } 960 switch (matchType) { 961 case 1: 962 return MatchType.Practice; 963 case 2: 964 return MatchType.Qualification; 965 case 3: 966 return MatchType.Elimination; 967 default: 968 return MatchType.None; 969 } 970 } 971 972 /** 973 * Get the match number. 974 * 975 * @return the match number 976 */ 977 public static int getMatchNumber() { 978 m_cacheDataMutex.lock(); 979 try { 980 return m_matchInfo.matchNumber; 981 } finally { 982 m_cacheDataMutex.unlock(); 983 } 984 } 985 986 /** 987 * Get the replay number. 988 * 989 * @return the replay number 990 */ 991 public static int getReplayNumber() { 992 m_cacheDataMutex.lock(); 993 try { 994 return m_matchInfo.replayNumber; 995 } finally { 996 m_cacheDataMutex.unlock(); 997 } 998 } 999 1000 /** 1001 * Get the current alliance from the FMS. 1002 * 1003 * @return the current alliance 1004 */ 1005 public static Alliance getAlliance() { 1006 AllianceStationID allianceStationID = HAL.getAllianceStation(); 1007 if (allianceStationID == null) { 1008 return Alliance.Invalid; 1009 } 1010 1011 switch (allianceStationID) { 1012 case Red1: 1013 case Red2: 1014 case Red3: 1015 return Alliance.Red; 1016 1017 case Blue1: 1018 case Blue2: 1019 case Blue3: 1020 return Alliance.Blue; 1021 1022 default: 1023 return Alliance.Invalid; 1024 } 1025 } 1026 1027 /** 1028 * Gets the location of the team's driver station controls. 1029 * 1030 * @return the location of the team's driver station controls: 1, 2, or 3 1031 */ 1032 public static int getLocation() { 1033 AllianceStationID allianceStationID = HAL.getAllianceStation(); 1034 if (allianceStationID == null) { 1035 return 0; 1036 } 1037 switch (allianceStationID) { 1038 case Red1: 1039 case Blue1: 1040 return 1; 1041 1042 case Red2: 1043 case Blue2: 1044 return 2; 1045 1046 case Blue3: 1047 case Red3: 1048 return 3; 1049 1050 default: 1051 return 0; 1052 } 1053 } 1054 1055 /** 1056 * Wait for new data from the driver station. 1057 * 1058 * <p>Checks if new control data has arrived since the last waitForData call on the current 1059 * thread. If new data has not arrived, returns immediately. 1060 */ 1061 public static void waitForData() { 1062 waitForData(0); 1063 } 1064 1065 /** 1066 * Wait for new data or for timeout, which ever comes first. If timeout is 0, wait for new data 1067 * only. 1068 * 1069 * <p>Checks if new control data has arrived since the last waitForData call on the current 1070 * thread. If new data has not arrived, returns immediately. 1071 * 1072 * @param timeoutSeconds The maximum time in seconds to wait. 1073 * @return true if there is new data, otherwise false 1074 */ 1075 public static boolean waitForData(double timeoutSeconds) { 1076 long startTimeMicroS = RobotController.getFPGATime(); 1077 long timeoutMicroS = (long) (timeoutSeconds * 1e6); 1078 m_waitForDataMutex.lock(); 1079 try { 1080 int currentCount = m_waitForDataCount; 1081 if (m_lastCount.get() != currentCount) { 1082 m_lastCount.set(currentCount); 1083 return true; 1084 } 1085 while (m_waitForDataCount == currentCount) { 1086 if (timeoutMicroS > 0) { 1087 long nowMicroS = RobotController.getFPGATime(); 1088 if (nowMicroS < startTimeMicroS + timeoutMicroS) { 1089 // We still have time to wait 1090 boolean signaled = 1091 m_waitForDataCond.await( 1092 startTimeMicroS + timeoutMicroS - nowMicroS, TimeUnit.MICROSECONDS); 1093 if (!signaled) { 1094 // Return false if a timeout happened 1095 return false; 1096 } 1097 } else { 1098 // Time has elapsed. 1099 return false; 1100 } 1101 } else { 1102 m_waitForDataCond.await(); 1103 } 1104 } 1105 m_lastCount.set(m_waitForDataCount); 1106 // Return true if we have received a proper signal 1107 return true; 1108 } catch (InterruptedException ex) { 1109 // return false on a thread interrupt 1110 Thread.currentThread().interrupt(); 1111 return false; 1112 } finally { 1113 m_waitForDataMutex.unlock(); 1114 } 1115 } 1116 1117 /** 1118 * Return the approximate match time. The FMS does not send an official match time to the robots, 1119 * but does send an approximate match time. The value will count down the time remaining in the 1120 * current period (auto or teleop). Warning: This is not an official time (so it cannot be used to 1121 * dispute ref calls or guarantee that a function will trigger before the match ends) The Practice 1122 * Match function of the DS approximates the behavior seen on the field. 1123 * 1124 * @return Time remaining in current match period (auto or teleop) in seconds 1125 */ 1126 public static double getMatchTime() { 1127 return HAL.getMatchTime(); 1128 } 1129 1130 /** 1131 * Only to be used to tell the Driver Station what code you claim to be executing for diagnostic 1132 * purposes only. 1133 * 1134 * @param entering If true, starting disabled code; if false, leaving disabled code 1135 */ 1136 public static void inDisabled(boolean entering) { 1137 m_userInDisabled = entering; 1138 } 1139 1140 /** 1141 * Only to be used to tell the Driver Station what code you claim to be executing for diagnostic 1142 * purposes only. 1143 * 1144 * @param entering If true, starting autonomous code; if false, leaving autonomous code 1145 */ 1146 public static void inAutonomous(boolean entering) { 1147 m_userInAutonomous = entering; 1148 } 1149 1150 /** 1151 * Only to be used to tell the Driver Station what code you claim to be executing for diagnostic 1152 * purposes only. 1153 * 1154 * @param entering If true, starting teleop code; if false, leaving teleop code 1155 * @deprecated Use {@link #inTeleop(boolean)} instead. 1156 */ 1157 @Deprecated(since = "2022", forRemoval = true) 1158 public static void inOperatorControl(boolean entering) { 1159 m_userInTeleop = entering; 1160 } 1161 1162 /** 1163 * Only to be used to tell the Driver Station what code you claim to be executing for diagnostic 1164 * purposes only. 1165 * 1166 * @param entering If true, starting teleop code; if false, leaving teleop code 1167 */ 1168 public static void inTeleop(boolean entering) { 1169 m_userInTeleop = entering; 1170 } 1171 1172 /** 1173 * Only to be used to tell the Driver Station what code you claim to be executing for diagnostic 1174 * purposes only. 1175 * 1176 * @param entering If true, starting test code; if false, leaving test code 1177 */ 1178 public static void inTest(boolean entering) { 1179 m_userInTest = entering; 1180 } 1181 1182 /** Forces waitForData() to return immediately. */ 1183 public static void wakeupWaitForData() { 1184 m_waitForDataMutex.lock(); 1185 try { 1186 m_waitForDataCount++; 1187 m_waitForDataCond.signalAll(); 1188 } finally { 1189 m_waitForDataMutex.unlock(); 1190 } 1191 } 1192 1193 /** 1194 * Allows the user to specify whether they want joystick connection warnings to be printed to the 1195 * console. This setting is ignored when the FMS is connected -- warnings will always be on in 1196 * that scenario. 1197 * 1198 * @param silence Whether warning messages should be silenced. 1199 */ 1200 public static void silenceJoystickConnectionWarning(boolean silence) { 1201 m_silenceJoystickWarning = silence; 1202 } 1203 1204 /** 1205 * Returns whether joystick connection warnings are silenced. This will always return false when 1206 * connected to the FMS. 1207 * 1208 * @return Whether joystick connection warnings are silenced. 1209 */ 1210 public static boolean isJoystickConnectionWarningSilenced() { 1211 return !isFMSAttached() && m_silenceJoystickWarning; 1212 } 1213 1214 /** 1215 * Copy data from the DS task for the user. If no new data exists, it will just be returned, 1216 * otherwise the data will be copied from the DS polling loop. 1217 */ 1218 protected static void getData() { 1219 // Get the status of all of the joysticks 1220 for (byte stick = 0; stick < kJoystickPorts; stick++) { 1221 m_joystickAxesCache[stick].m_count = 1222 HAL.getJoystickAxes(stick, m_joystickAxesCache[stick].m_axes); 1223 m_joystickPOVsCache[stick].m_count = 1224 HAL.getJoystickPOVs(stick, m_joystickPOVsCache[stick].m_povs); 1225 m_joystickButtonsCache[stick].m_buttons = HAL.getJoystickButtons(stick, m_buttonCountBuffer); 1226 m_joystickButtonsCache[stick].m_count = m_buttonCountBuffer.get(0); 1227 } 1228 1229 HAL.getMatchInfo(m_matchInfoCache); 1230 1231 // Force a control word update, to make sure the data is the newest. 1232 updateControlWord(true); 1233 1234 // lock joystick mutex to swap cache data 1235 m_cacheDataMutex.lock(); 1236 try { 1237 for (int i = 0; i < kJoystickPorts; i++) { 1238 // If buttons weren't pressed and are now, set flags in m_buttonsPressed 1239 m_joystickButtonsPressed[i] |= 1240 ~m_joystickButtons[i].m_buttons & m_joystickButtonsCache[i].m_buttons; 1241 1242 // If buttons were pressed and aren't now, set flags in m_buttonsReleased 1243 m_joystickButtonsReleased[i] |= 1244 m_joystickButtons[i].m_buttons & ~m_joystickButtonsCache[i].m_buttons; 1245 } 1246 1247 // move cache to actual data 1248 HALJoystickAxes[] currentAxes = m_joystickAxes; 1249 m_joystickAxes = m_joystickAxesCache; 1250 m_joystickAxesCache = currentAxes; 1251 1252 HALJoystickButtons[] currentButtons = m_joystickButtons; 1253 m_joystickButtons = m_joystickButtonsCache; 1254 m_joystickButtonsCache = currentButtons; 1255 1256 HALJoystickPOVs[] currentPOVs = m_joystickPOVs; 1257 m_joystickPOVs = m_joystickPOVsCache; 1258 m_joystickPOVsCache = currentPOVs; 1259 1260 MatchInfoData currentInfo = m_matchInfo; 1261 m_matchInfo = m_matchInfoCache; 1262 m_matchInfoCache = currentInfo; 1263 } finally { 1264 m_cacheDataMutex.unlock(); 1265 } 1266 1267 wakeupWaitForData(); 1268 m_matchDataSender.sendMatchData(); 1269 } 1270 1271 /** 1272 * Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm 1273 * the DS. 1274 */ 1275 private static void reportJoystickUnpluggedError(String message) { 1276 double currentTime = Timer.getFPGATimestamp(); 1277 if (currentTime > m_nextMessageTime) { 1278 reportError(message, false); 1279 m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL; 1280 } 1281 } 1282 1283 /** 1284 * Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm 1285 * the DS. 1286 */ 1287 private static void reportJoystickUnpluggedWarning(String message) { 1288 if (isFMSAttached() || !m_silenceJoystickWarning) { 1289 double currentTime = Timer.getFPGATimestamp(); 1290 if (currentTime > m_nextMessageTime) { 1291 reportWarning(message, false); 1292 m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL; 1293 } 1294 } 1295 } 1296 1297 /** Provides the service routine for the DS polling m_thread. */ 1298 private static void run() { 1299 int safetyCounter = 0; 1300 while (m_threadKeepAlive) { 1301 HAL.waitForDSData(); 1302 getData(); 1303 1304 if (isDisabled()) { 1305 safetyCounter = 0; 1306 } 1307 1308 safetyCounter++; 1309 if (safetyCounter >= 4) { 1310 MotorSafety.checkMotors(); 1311 safetyCounter = 0; 1312 } 1313 if (m_userInDisabled) { 1314 HAL.observeUserProgramDisabled(); 1315 } 1316 if (m_userInAutonomous) { 1317 HAL.observeUserProgramAutonomous(); 1318 } 1319 if (m_userInTeleop) { 1320 HAL.observeUserProgramTeleop(); 1321 } 1322 if (m_userInTest) { 1323 HAL.observeUserProgramTest(); 1324 } 1325 } 1326 } 1327 1328 /** 1329 * Forces a control word cache update, and update the passed in control word. 1330 * 1331 * @param word Word to update. 1332 */ 1333 public static void updateControlWordFromCache(ControlWord word) { 1334 synchronized (m_controlWordMutex) { 1335 updateControlWord(true); 1336 word.update(m_controlWordCache); 1337 } 1338 } 1339 1340 /** 1341 * Updates the data in the control word cache. Updates if the force parameter is set, or if 50ms 1342 * have passed since the last update. 1343 * 1344 * @param force True to force an update to the cache, otherwise update if 50ms have passed. 1345 */ 1346 private static void updateControlWord(boolean force) { 1347 long now = System.currentTimeMillis(); 1348 synchronized (m_controlWordMutex) { 1349 if (now - m_lastControlWordUpdate > 50 || force) { 1350 HAL.getControlWord(m_controlWordCache); 1351 m_lastControlWordUpdate = now; 1352 } 1353 } 1354 } 1355}