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}