mirror of
https://github.com/Team4388/2022NoWayHome.git
synced 2026-06-09 00:38:05 -06:00
a95c97e4fe
Reduce the amount of strings created when logging Add a temporary to do list to the read me file
1611 lines
49 KiB
Java
1611 lines
49 KiB
Java
// Copyright (c) FIRST and other WPILib contributors.
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
package edu.wpi.first.wpilibj;
|
|
|
|
import edu.wpi.first.hal.AllianceStationID;
|
|
import edu.wpi.first.hal.ControlWord;
|
|
import edu.wpi.first.hal.HAL;
|
|
import edu.wpi.first.hal.MatchInfoData;
|
|
import edu.wpi.first.networktables.NetworkTable;
|
|
import edu.wpi.first.networktables.NetworkTableEntry;
|
|
import edu.wpi.first.networktables.NetworkTableInstance;
|
|
import edu.wpi.first.util.WPIUtilJNI;
|
|
import edu.wpi.first.util.datalog.BooleanArrayLogEntry;
|
|
import edu.wpi.first.util.datalog.BooleanLogEntry;
|
|
import edu.wpi.first.util.datalog.DataLog;
|
|
import edu.wpi.first.util.datalog.FloatArrayLogEntry;
|
|
import edu.wpi.first.util.datalog.IntegerArrayLogEntry;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.locks.Condition;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
/** Provide access to the network communication data to / from the Driver Station. */
|
|
public class DriverStation {
|
|
/** Number of Joystick Ports. */
|
|
public static final int kJoystickPorts = 6;
|
|
|
|
private static class HALJoystickButtons {
|
|
public int m_buttons;
|
|
public byte m_count;
|
|
}
|
|
|
|
private static class HALJoystickAxes {
|
|
public float[] m_axes;
|
|
public short m_count;
|
|
|
|
HALJoystickAxes(int count) {
|
|
m_axes = new float[count];
|
|
}
|
|
}
|
|
|
|
private static class HALJoystickPOVs {
|
|
public short[] m_povs;
|
|
public short m_count;
|
|
|
|
HALJoystickPOVs(int count) {
|
|
m_povs = new short[count];
|
|
for (int i = 0; i < count; i++) {
|
|
m_povs[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** The robot alliance that the robot is a part of. */
|
|
public enum Alliance {
|
|
Red,
|
|
Blue,
|
|
Invalid
|
|
}
|
|
|
|
public enum MatchType {
|
|
None,
|
|
Practice,
|
|
Qualification,
|
|
Elimination
|
|
}
|
|
|
|
private static final double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0;
|
|
private static double m_nextMessageTime;
|
|
|
|
private static class DriverStationTask implements Runnable {
|
|
DriverStationTask() {}
|
|
|
|
@Override
|
|
public void run() {
|
|
DriverStation.run();
|
|
}
|
|
} /* DriverStationTask */
|
|
|
|
private static class MatchDataSender {
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTable table;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry typeMetadata;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry gameSpecificMessage;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry eventName;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry matchNumber;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry replayNumber;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry matchType;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry alliance;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry station;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
NetworkTableEntry controlWord;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
boolean oldIsRedAlliance = true;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
int oldStationNumber = 1;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
String oldEventName = "";
|
|
|
|
@SuppressWarnings("MemberName")
|
|
String oldGameSpecificMessage = "";
|
|
|
|
@SuppressWarnings("MemberName")
|
|
int oldMatchNumber;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
int oldReplayNumber;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
int oldMatchType;
|
|
|
|
@SuppressWarnings("MemberName")
|
|
int oldControlWord;
|
|
|
|
MatchDataSender() {
|
|
table = NetworkTableInstance.getDefault().getTable("FMSInfo");
|
|
typeMetadata = table.getEntry(".type");
|
|
typeMetadata.forceSetString("FMSInfo");
|
|
gameSpecificMessage = table.getEntry("GameSpecificMessage");
|
|
gameSpecificMessage.forceSetString("");
|
|
eventName = table.getEntry("EventName");
|
|
eventName.forceSetString("");
|
|
matchNumber = table.getEntry("MatchNumber");
|
|
matchNumber.forceSetDouble(0);
|
|
replayNumber = table.getEntry("ReplayNumber");
|
|
replayNumber.forceSetDouble(0);
|
|
matchType = table.getEntry("MatchType");
|
|
matchType.forceSetDouble(0);
|
|
alliance = table.getEntry("IsRedAlliance");
|
|
alliance.forceSetBoolean(true);
|
|
station = table.getEntry("StationNumber");
|
|
station.forceSetDouble(1);
|
|
controlWord = table.getEntry("FMSControlData");
|
|
controlWord.forceSetDouble(0);
|
|
}
|
|
|
|
private void sendMatchData() {
|
|
AllianceStationID allianceID = HAL.getAllianceStation();
|
|
boolean isRedAlliance = false;
|
|
int stationNumber = 1;
|
|
switch (allianceID) {
|
|
case Blue1:
|
|
isRedAlliance = false;
|
|
stationNumber = 1;
|
|
break;
|
|
case Blue2:
|
|
isRedAlliance = false;
|
|
stationNumber = 2;
|
|
break;
|
|
case Blue3:
|
|
isRedAlliance = false;
|
|
stationNumber = 3;
|
|
break;
|
|
case Red1:
|
|
isRedAlliance = true;
|
|
stationNumber = 1;
|
|
break;
|
|
case Red2:
|
|
isRedAlliance = true;
|
|
stationNumber = 2;
|
|
break;
|
|
default:
|
|
isRedAlliance = true;
|
|
stationNumber = 3;
|
|
break;
|
|
}
|
|
|
|
String currentEventName;
|
|
String currentGameSpecificMessage;
|
|
int currentMatchNumber;
|
|
int currentReplayNumber;
|
|
int currentMatchType;
|
|
int currentControlWord;
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
currentEventName = DriverStation.m_matchInfo.eventName;
|
|
currentGameSpecificMessage = DriverStation.m_matchInfo.gameSpecificMessage;
|
|
currentMatchNumber = DriverStation.m_matchInfo.matchNumber;
|
|
currentReplayNumber = DriverStation.m_matchInfo.replayNumber;
|
|
currentMatchType = DriverStation.m_matchInfo.matchType;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
currentControlWord = HAL.nativeGetControlWord();
|
|
|
|
if (oldIsRedAlliance != isRedAlliance) {
|
|
alliance.setBoolean(isRedAlliance);
|
|
oldIsRedAlliance = isRedAlliance;
|
|
}
|
|
if (oldStationNumber != stationNumber) {
|
|
station.setDouble(stationNumber);
|
|
oldStationNumber = stationNumber;
|
|
}
|
|
if (!oldEventName.equals(currentEventName)) {
|
|
eventName.setString(currentEventName);
|
|
oldEventName = currentEventName;
|
|
}
|
|
if (!oldGameSpecificMessage.equals(currentGameSpecificMessage)) {
|
|
gameSpecificMessage.setString(currentGameSpecificMessage);
|
|
oldGameSpecificMessage = currentGameSpecificMessage;
|
|
}
|
|
if (currentMatchNumber != oldMatchNumber) {
|
|
matchNumber.setDouble(currentMatchNumber);
|
|
oldMatchNumber = currentMatchNumber;
|
|
}
|
|
if (currentReplayNumber != oldReplayNumber) {
|
|
replayNumber.setDouble(currentReplayNumber);
|
|
oldReplayNumber = currentReplayNumber;
|
|
}
|
|
if (currentMatchType != oldMatchType) {
|
|
matchType.setDouble(currentMatchType);
|
|
oldMatchType = currentMatchType;
|
|
}
|
|
if (currentControlWord != oldControlWord) {
|
|
controlWord.setDouble(currentControlWord);
|
|
oldControlWord = currentControlWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class JoystickLogSender {
|
|
JoystickLogSender(DataLog log, int stick, long timestamp) {
|
|
m_stick = stick;
|
|
|
|
m_logButtons = new BooleanArrayLogEntry(log, "DS:joystick" + stick + "/buttons", timestamp);
|
|
m_logAxes = new FloatArrayLogEntry(log, "DS:joystick" + stick + "/axes", timestamp);
|
|
m_logPOVs = new IntegerArrayLogEntry(log, "DS:joystick" + stick + "/povs", timestamp);
|
|
|
|
appendButtons(timestamp);
|
|
appendAxes(timestamp);
|
|
appendPOVs(timestamp);
|
|
}
|
|
|
|
public void send(long timestamp) {
|
|
if (m_joystickButtonsCache[m_stick].m_count != m_joystickButtons[m_stick].m_count
|
|
|| m_joystickButtonsCache[m_stick].m_buttons != m_joystickButtons[m_stick].m_buttons) {
|
|
appendButtons(timestamp);
|
|
}
|
|
|
|
if (m_joystickAxesCache[m_stick].m_count != m_joystickAxes[m_stick].m_count) {
|
|
appendAxes(timestamp);
|
|
} else {
|
|
int count = m_joystickAxesCache[m_stick].m_count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (m_joystickAxesCache[m_stick].m_axes[i] != m_joystickAxes[m_stick].m_axes[i]) {
|
|
appendAxes(timestamp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_joystickPOVsCache[m_stick].m_count != m_joystickPOVs[m_stick].m_count) {
|
|
appendPOVs(timestamp);
|
|
} else {
|
|
int count = m_joystickPOVsCache[m_stick].m_count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (m_joystickPOVsCache[m_stick].m_povs[i] != m_joystickPOVs[m_stick].m_povs[i]) {
|
|
appendPOVs(timestamp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void appendButtons(long timestamp) {
|
|
int count = m_joystickButtonsCache[m_stick].m_count;
|
|
if (m_sizedButtons == null || m_sizedButtons.length != count) {
|
|
m_sizedButtons = new boolean[count];
|
|
}
|
|
int buttons = m_joystickButtonsCache[m_stick].m_buttons;
|
|
for (int i = 0; i < count; i++) {
|
|
m_sizedButtons[i] = (buttons & (1 << i)) != 0;
|
|
}
|
|
m_logButtons.append(m_sizedButtons, timestamp);
|
|
}
|
|
|
|
void appendAxes(long timestamp) {
|
|
int count = m_joystickAxesCache[m_stick].m_count;
|
|
if (m_sizedAxes == null || m_sizedAxes.length != count) {
|
|
m_sizedAxes = new float[count];
|
|
}
|
|
System.arraycopy(m_joystickAxesCache[m_stick].m_axes, 0, m_sizedAxes, 0, count);
|
|
m_logAxes.append(m_sizedAxes, timestamp);
|
|
}
|
|
|
|
void appendPOVs(long timestamp) {
|
|
int count = m_joystickPOVsCache[m_stick].m_count;
|
|
if (m_sizedPOVs == null || m_sizedPOVs.length != count) {
|
|
m_sizedPOVs = new long[count];
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
m_sizedPOVs[i] = m_joystickPOVsCache[m_stick].m_povs[i];
|
|
}
|
|
m_logPOVs.append(m_sizedPOVs, timestamp);
|
|
}
|
|
|
|
final int m_stick;
|
|
boolean[] m_sizedButtons;
|
|
float[] m_sizedAxes;
|
|
long[] m_sizedPOVs;
|
|
final BooleanArrayLogEntry m_logButtons;
|
|
final FloatArrayLogEntry m_logAxes;
|
|
final IntegerArrayLogEntry m_logPOVs;
|
|
}
|
|
|
|
private static class DataLogSender {
|
|
DataLogSender(DataLog log, boolean logJoysticks, long timestamp) {
|
|
m_logEnabled = new BooleanLogEntry(log, "DS:enabled", timestamp);
|
|
m_logAutonomous = new BooleanLogEntry(log, "DS:autonomous", timestamp);
|
|
m_logTest = new BooleanLogEntry(log, "DS:test", timestamp);
|
|
m_logEstop = new BooleanLogEntry(log, "DS:estop", timestamp);
|
|
|
|
// append initial control word values
|
|
m_wasEnabled = m_controlWordCache.getEnabled();
|
|
m_wasAutonomous = m_controlWordCache.getAutonomous();
|
|
m_wasTest = m_controlWordCache.getTest();
|
|
m_wasEstop = m_controlWordCache.getEStop();
|
|
|
|
m_logEnabled.append(m_wasEnabled, timestamp);
|
|
m_logAutonomous.append(m_wasAutonomous, timestamp);
|
|
m_logTest.append(m_wasTest, timestamp);
|
|
m_logEstop.append(m_wasEstop, timestamp);
|
|
|
|
if (logJoysticks) {
|
|
m_joysticks = new JoystickLogSender[kJoystickPorts];
|
|
for (int i = 0; i < kJoystickPorts; i++) {
|
|
m_joysticks[i] = new JoystickLogSender(log, i, timestamp);
|
|
}
|
|
} else {
|
|
m_joysticks = new JoystickLogSender[0];
|
|
}
|
|
}
|
|
|
|
public void send(long timestamp) {
|
|
// append control word value changes
|
|
boolean enabled = m_controlWordCache.getEnabled();
|
|
if (enabled != m_wasEnabled) {
|
|
m_logEnabled.append(enabled, timestamp);
|
|
}
|
|
m_wasEnabled = enabled;
|
|
|
|
boolean autonomous = m_controlWordCache.getAutonomous();
|
|
if (autonomous != m_wasAutonomous) {
|
|
m_logAutonomous.append(autonomous, timestamp);
|
|
}
|
|
m_wasAutonomous = autonomous;
|
|
|
|
boolean test = m_controlWordCache.getTest();
|
|
if (test != m_wasTest) {
|
|
m_logTest.append(test, timestamp);
|
|
}
|
|
m_wasTest = test;
|
|
|
|
boolean estop = m_controlWordCache.getEStop();
|
|
if (estop != m_wasEstop) {
|
|
m_logEstop.append(estop, timestamp);
|
|
}
|
|
m_wasEstop = estop;
|
|
|
|
// append joystick value changes
|
|
for (JoystickLogSender joystick : m_joysticks) {
|
|
joystick.send(timestamp);
|
|
}
|
|
}
|
|
|
|
boolean m_wasEnabled;
|
|
boolean m_wasAutonomous;
|
|
boolean m_wasTest;
|
|
boolean m_wasEstop;
|
|
final BooleanLogEntry m_logEnabled;
|
|
final BooleanLogEntry m_logAutonomous;
|
|
final BooleanLogEntry m_logTest;
|
|
final BooleanLogEntry m_logEstop;
|
|
|
|
final JoystickLogSender[] m_joysticks;
|
|
}
|
|
|
|
private static DriverStation instance = new DriverStation();
|
|
|
|
// Joystick User Data
|
|
private static HALJoystickAxes[] m_joystickAxes = new HALJoystickAxes[kJoystickPorts];
|
|
private static HALJoystickPOVs[] m_joystickPOVs = new HALJoystickPOVs[kJoystickPorts];
|
|
private static HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[kJoystickPorts];
|
|
private static MatchInfoData m_matchInfo = new MatchInfoData();
|
|
|
|
// Joystick Cached Data
|
|
private static HALJoystickAxes[] m_joystickAxesCache = new HALJoystickAxes[kJoystickPorts];
|
|
private static HALJoystickPOVs[] m_joystickPOVsCache = new HALJoystickPOVs[kJoystickPorts];
|
|
private static HALJoystickButtons[] m_joystickButtonsCache =
|
|
new HALJoystickButtons[kJoystickPorts];
|
|
private static MatchInfoData m_matchInfoCache = new MatchInfoData();
|
|
|
|
// Joystick button rising/falling edge flags
|
|
private static int[] m_joystickButtonsPressed = new int[kJoystickPorts];
|
|
private static int[] m_joystickButtonsReleased = new int[kJoystickPorts];
|
|
|
|
// preallocated byte buffer for button count
|
|
private static final ByteBuffer m_buttonCountBuffer = ByteBuffer.allocateDirect(1);
|
|
|
|
private static final MatchDataSender m_matchDataSender;
|
|
private static DataLogSender m_dataLogSender;
|
|
|
|
// Internal Driver Station thread
|
|
private static Thread m_thread;
|
|
|
|
private static volatile boolean m_threadKeepAlive = true;
|
|
|
|
private static final ReentrantLock m_cacheDataMutex = new ReentrantLock();
|
|
|
|
private static final Lock m_waitForDataMutex;
|
|
private static final Condition m_waitForDataCond;
|
|
private static int m_waitForDataCount;
|
|
private static final ThreadLocal<Integer> m_lastCount = ThreadLocal.withInitial(() -> 0);
|
|
|
|
private static boolean m_silenceJoystickWarning;
|
|
|
|
// Robot state status variables
|
|
private static boolean m_userInDisabled;
|
|
private static boolean m_userInAutonomous;
|
|
private static boolean m_userInTeleop;
|
|
private static boolean m_userInTest;
|
|
|
|
// Control word variables
|
|
private static final ReentrantLock m_controlWordMutex = new ReentrantLock();
|
|
private static final ControlWord m_controlWordCache;
|
|
private static long m_lastControlWordUpdate;
|
|
|
|
/**
|
|
* Gets an instance of the DriverStation.
|
|
*
|
|
* @return The DriverStation.
|
|
* @deprecated Use the static methods
|
|
*/
|
|
@Deprecated
|
|
public static DriverStation getInstance() {
|
|
return DriverStation.instance;
|
|
}
|
|
|
|
/**
|
|
* DriverStation constructor.
|
|
*
|
|
* <p>The single DriverStation instance is created statically with the instance static member
|
|
* variable.
|
|
*/
|
|
private DriverStation() {}
|
|
|
|
static {
|
|
HAL.initialize(500, 0);
|
|
m_waitForDataCount = 0;
|
|
m_waitForDataMutex = new ReentrantLock();
|
|
m_waitForDataCond = m_waitForDataMutex.newCondition();
|
|
|
|
for (int i = 0; i < kJoystickPorts; i++) {
|
|
m_joystickButtons[i] = new HALJoystickButtons();
|
|
m_joystickAxes[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes);
|
|
m_joystickPOVs[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs);
|
|
|
|
m_joystickButtonsCache[i] = new HALJoystickButtons();
|
|
m_joystickAxesCache[i] = new HALJoystickAxes(HAL.kMaxJoystickAxes);
|
|
m_joystickPOVsCache[i] = new HALJoystickPOVs(HAL.kMaxJoystickPOVs);
|
|
}
|
|
|
|
m_controlWordCache = new ControlWord();
|
|
m_lastControlWordUpdate = 0;
|
|
|
|
m_matchDataSender = new MatchDataSender();
|
|
|
|
m_thread = new Thread(new DriverStationTask(), "FRCDriverStation");
|
|
m_thread.setPriority((Thread.NORM_PRIORITY + Thread.MAX_PRIORITY) / 2);
|
|
|
|
m_thread.start();
|
|
}
|
|
|
|
/** Kill the thread. */
|
|
public static synchronized void release() {
|
|
m_threadKeepAlive = false;
|
|
if (m_thread != null) {
|
|
try {
|
|
m_thread.join();
|
|
} catch (InterruptedException ex) {
|
|
Thread.currentThread().interrupt();
|
|
}
|
|
m_thread = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Report error to Driver Station. Optionally appends Stack trace to error message.
|
|
*
|
|
* @param error The error to report.
|
|
* @param printTrace If true, append stack trace to error string
|
|
*/
|
|
public static void reportError(String error, boolean printTrace) {
|
|
reportErrorImpl(true, 1, error, printTrace);
|
|
}
|
|
|
|
/**
|
|
* Report error to Driver Station. Appends provided stack trace to error message.
|
|
*
|
|
* @param error The error to report.
|
|
* @param stackTrace The stack trace to append
|
|
*/
|
|
public static void reportError(String error, StackTraceElement[] stackTrace) {
|
|
reportErrorImpl(true, 1, error, stackTrace);
|
|
}
|
|
|
|
/**
|
|
* Report warning to Driver Station. Optionally appends Stack trace to warning message.
|
|
*
|
|
* @param warning The warning to report.
|
|
* @param printTrace If true, append stack trace to warning string
|
|
*/
|
|
public static void reportWarning(String warning, boolean printTrace) {
|
|
reportErrorImpl(false, 1, warning, printTrace);
|
|
}
|
|
|
|
/**
|
|
* Report warning to Driver Station. Appends provided stack trace to warning message.
|
|
*
|
|
* @param warning The warning to report.
|
|
* @param stackTrace The stack trace to append
|
|
*/
|
|
public static void reportWarning(String warning, StackTraceElement[] stackTrace) {
|
|
reportErrorImpl(false, 1, warning, stackTrace);
|
|
}
|
|
|
|
private static void reportErrorImpl(boolean isError, int code, String error, boolean printTrace) {
|
|
reportErrorImpl(isError, code, error, printTrace, Thread.currentThread().getStackTrace(), 3);
|
|
}
|
|
|
|
private static void reportErrorImpl(
|
|
boolean isError, int code, String error, StackTraceElement[] stackTrace) {
|
|
reportErrorImpl(isError, code, error, true, stackTrace, 0);
|
|
}
|
|
|
|
private static void reportErrorImpl(
|
|
boolean isError,
|
|
int code,
|
|
String error,
|
|
boolean printTrace,
|
|
StackTraceElement[] stackTrace,
|
|
int stackTraceFirst) {
|
|
if (frc4388.utility.AnsiLogging.halLoggerHandler != null) {
|
|
if (!frc4388.utility.AnsiLogging.LEVEL.equals(java.util.logging.Level.OFF)) {
|
|
java.util.logging.LogRecord logRecord = new java.util.logging.LogRecord(isError ? java.util.logging.Level.SEVERE : java.util.logging.Level.FINER, error.stripTrailing());
|
|
logRecord.setLoggerName("HAL");
|
|
if (!frc4388.utility.AnsiLogging.halLoggerHandler.isLoggable(logRecord)) return;
|
|
java.util.Optional.ofNullable(stackTrace).filter(s -> s.length >= stackTraceFirst + 1).map(s -> java.util.Arrays.copyOfRange(s, Math.min(Math.max(0, stackTraceFirst), s.length - 1), s.length - 1)).ifPresent(presentStackTrace -> {
|
|
logRecord.setSourceMethodName(presentStackTrace[0].getMethodName());
|
|
String throwableMessage;
|
|
if (presentStackTrace[0].toString().equals("edu.wpi.first.wpilibj.Tracer.lambda$printEpochs$0(Tracer.java:63)")) {
|
|
throwableMessage = "Epochs" + System.lineSeparator() + logRecord.getMessage();
|
|
presentStackTrace = new java.lang.StackTraceElement[0];
|
|
logRecord.setLevel(java.util.logging.Level.FINEST);
|
|
logRecord.setMessage("Execution times:");
|
|
} else if (printTrace) {
|
|
long lineCount = logRecord.getMessage().lines().count();
|
|
throwableMessage = (lineCount > 1 ? logRecord.getMessage().lines().findFirst().map(s -> s + " + " + lineCount + " more lines...").orElse("") : logRecord.getMessage()).stripLeading();
|
|
} else return;
|
|
java.lang.Throwable throwable = new java.lang.Throwable(throwableMessage);
|
|
throwable.setStackTrace(presentStackTrace);
|
|
logRecord.setThrown(throwable);
|
|
});
|
|
if (!frc4388.utility.AnsiLogging.halLoggerHandler.isLoggable(logRecord)) return;
|
|
frc4388.utility.AnsiLogging.halLoggerHandler.publish(logRecord);
|
|
}
|
|
} else {
|
|
String locString;
|
|
if (stackTrace.length >= stackTraceFirst + 1) {
|
|
locString = stackTrace[stackTraceFirst].toString();
|
|
} else {
|
|
locString = "";
|
|
}
|
|
StringBuilder traceString = new StringBuilder();
|
|
if (printTrace) {
|
|
boolean haveLoc = false;
|
|
for (int i = stackTraceFirst; i < stackTrace.length; i++) {
|
|
String loc = stackTrace[i].toString();
|
|
traceString.append("\tat ").append(loc).append('\n');
|
|
// get first user function
|
|
if (!haveLoc && !loc.startsWith("edu.wpi.first")) {
|
|
locString = loc;
|
|
haveLoc = true;
|
|
}
|
|
}
|
|
}
|
|
HAL.sendError(isError, code, false, error, locString, traceString.toString(), true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The state of one joystick button. Button indexes begin at 1.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @param button The button index, beginning at 1.
|
|
* @return The state of the joystick button.
|
|
*/
|
|
public static boolean getStickButton(final int stick, final int button) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
if (button <= 0) {
|
|
reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n");
|
|
return false;
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (button <= m_joystickButtons[stick].m_count) {
|
|
return (m_joystickButtons[stick].m_buttons & 1 << (button - 1)) != 0;
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
reportJoystickUnpluggedWarning(
|
|
"Joystick Button "
|
|
+ button
|
|
+ " on port "
|
|
+ stick
|
|
+ " not available, check if controller is plugged in");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Whether one joystick button was pressed since the last check. Button indexes begin at 1.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @param button The button index, beginning at 1.
|
|
* @return Whether the joystick button was pressed since the last check.
|
|
*/
|
|
public static boolean getStickButtonPressed(final int stick, final int button) {
|
|
if (button <= 0) {
|
|
reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n");
|
|
return false;
|
|
}
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (button <= m_joystickButtons[stick].m_count) {
|
|
// If button was pressed, clear flag and return true
|
|
if ((m_joystickButtonsPressed[stick] & 1 << (button - 1)) != 0) {
|
|
m_joystickButtonsPressed[stick] &= ~(1 << (button - 1));
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
reportJoystickUnpluggedWarning(
|
|
"Joystick Button "
|
|
+ button
|
|
+ " on port "
|
|
+ stick
|
|
+ " not available, check if controller is plugged in");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Whether one joystick button was released since the last check. Button indexes begin at 1.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @param button The button index, beginning at 1.
|
|
* @return Whether the joystick button was released since the last check.
|
|
*/
|
|
public static boolean getStickButtonReleased(final int stick, final int button) {
|
|
if (button <= 0) {
|
|
reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java\n");
|
|
return false;
|
|
}
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (button <= m_joystickButtons[stick].m_count) {
|
|
// If button was released, clear flag and return true
|
|
if ((m_joystickButtonsReleased[stick] & 1 << (button - 1)) != 0) {
|
|
m_joystickButtonsReleased[stick] &= ~(1 << (button - 1));
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
reportJoystickUnpluggedWarning(
|
|
"Joystick Button "
|
|
+ button
|
|
+ " on port "
|
|
+ stick
|
|
+ " not available, check if controller is plugged in");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the value of the axis on a joystick. This depends on the mapping of the joystick connected
|
|
* to the specified port.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @param axis The analog axis value to read from the joystick.
|
|
* @return The value of the axis on the joystick.
|
|
*/
|
|
public static double getStickAxis(int stick, int axis) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
if (axis < 0 || axis >= HAL.kMaxJoystickAxes) {
|
|
throw new IllegalArgumentException("Joystick axis is out of range");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (axis < m_joystickAxes[stick].m_count) {
|
|
return m_joystickAxes[stick].m_axes[axis];
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
reportJoystickUnpluggedWarning(
|
|
"Joystick axis "
|
|
+ axis
|
|
+ " on port "
|
|
+ stick
|
|
+ " not available, check if controller is plugged in");
|
|
return 0.0;
|
|
}
|
|
|
|
/**
|
|
* Get the state of a POV on the joystick.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @param pov The POV to read.
|
|
* @return the angle of the POV in degrees, or -1 if the POV is not pressed.
|
|
*/
|
|
public static int getStickPOV(int stick, int pov) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
if (pov < 0 || pov >= HAL.kMaxJoystickPOVs) {
|
|
throw new IllegalArgumentException("Joystick POV is out of range");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (pov < m_joystickPOVs[stick].m_count) {
|
|
return m_joystickPOVs[stick].m_povs[pov];
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
reportJoystickUnpluggedWarning(
|
|
"Joystick POV "
|
|
+ pov
|
|
+ " on port "
|
|
+ stick
|
|
+ " not available, check if controller is plugged in");
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* The state of the buttons on the joystick.
|
|
*
|
|
* @param stick The joystick to read.
|
|
* @return The state of the buttons on the joystick.
|
|
*/
|
|
public static int getStickButtons(final int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_joystickButtons[stick].m_buttons;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the number of axes on a given joystick port.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return The number of axes on the indicated joystick
|
|
*/
|
|
public static int getStickAxisCount(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_joystickAxes[stick].m_count;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the number of POVs on a given joystick port.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return The number of POVs on the indicated joystick
|
|
*/
|
|
public static int getStickPOVCount(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_joystickPOVs[stick].m_count;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the number of buttons on a joystick.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return The number of buttons on the indicated joystick
|
|
*/
|
|
public static int getStickButtonCount(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_joystickButtons[stick].m_count;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the value of isXbox on a joystick.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return A boolean that returns the value of isXbox
|
|
*/
|
|
public static boolean getJoystickIsXbox(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
return HAL.getJoystickIsXbox((byte) stick) == 1;
|
|
}
|
|
|
|
/**
|
|
* Gets the value of type on a joystick.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return The value of type
|
|
*/
|
|
public static int getJoystickType(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
return HAL.getJoystickType((byte) stick);
|
|
}
|
|
|
|
/**
|
|
* Gets the name of the joystick at a port.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return The value of name
|
|
*/
|
|
public static String getJoystickName(int stick) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
return HAL.getJoystickName((byte) stick);
|
|
}
|
|
|
|
/**
|
|
* Returns the types of Axes on a given joystick port.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @param axis The target axis
|
|
* @return What type of axis the axis is reporting to be
|
|
*/
|
|
public static int getJoystickAxisType(int stick, int axis) {
|
|
if (stick < 0 || stick >= kJoystickPorts) {
|
|
throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
|
|
}
|
|
|
|
return HAL.getJoystickAxisType((byte) stick, (byte) axis);
|
|
}
|
|
|
|
/**
|
|
* Returns if a joystick is connected to the Driver Station.
|
|
*
|
|
* <p>This makes a best effort guess by looking at the reported number of axis, buttons, and POVs
|
|
* attached.
|
|
*
|
|
* @param stick The joystick port number
|
|
* @return true if a joystick is connected
|
|
*/
|
|
public static boolean isJoystickConnected(int stick) {
|
|
return getStickAxisCount(stick) > 0
|
|
|| getStickButtonCount(stick) > 0
|
|
|| getStickPOVCount(stick) > 0;
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be enabled.
|
|
*
|
|
* @return True if the robot is enabled, false otherwise.
|
|
*/
|
|
public static boolean isEnabled() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getEnabled() && m_controlWordCache.getDSAttached();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be disabled.
|
|
*
|
|
* @return True if the robot should be disabled, false otherwise.
|
|
*/
|
|
public static boolean isDisabled() {
|
|
return !isEnabled();
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Robot is e-stopped.
|
|
*
|
|
* @return True if the robot is e-stopped, false otherwise.
|
|
*/
|
|
public static boolean isEStopped() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getEStop();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* autonomous mode.
|
|
*
|
|
* @return True if autonomous mode should be enabled, false otherwise.
|
|
*/
|
|
public static boolean isAutonomous() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getAutonomous();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* autonomous mode and enabled.
|
|
*
|
|
* @return True if autonomous should be set and the robot should be enabled.
|
|
*/
|
|
public static boolean isAutonomousEnabled() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getAutonomous() && m_controlWordCache.getEnabled();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* operator-controlled mode.
|
|
*
|
|
* @return True if operator-controlled mode should be enabled, false otherwise.
|
|
* @deprecated Use isTeleop() instead.
|
|
*/
|
|
@Deprecated(since = "2022", forRemoval = true)
|
|
public static boolean isOperatorControl() {
|
|
return isTeleop();
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* operator-controlled mode.
|
|
*
|
|
* @return True if operator-controlled mode should be enabled, false otherwise.
|
|
*/
|
|
public static boolean isTeleop() {
|
|
return !(isAutonomous() || isTest());
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* operator-controller mode and enabled.
|
|
*
|
|
* @return True if operator-controlled mode should be set and the robot should be enabled.
|
|
* @deprecated Use isTeleopEnabled() instead.
|
|
*/
|
|
@Deprecated(since = "2022", forRemoval = true)
|
|
public static boolean isOperatorControlEnabled() {
|
|
return isTeleopEnabled();
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in
|
|
* operator-controller mode and enabled.
|
|
*
|
|
* @return True if operator-controlled mode should be set and the robot should be enabled.
|
|
*/
|
|
public static boolean isTeleopEnabled() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return !m_controlWordCache.getAutonomous()
|
|
&& !m_controlWordCache.getTest()
|
|
&& m_controlWordCache.getEnabled();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station requires the robot to be running in test
|
|
* mode.
|
|
*
|
|
* @return True if test mode should be enabled, false otherwise.
|
|
*/
|
|
public static boolean isTest() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getTest();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a value indicating whether the Driver Station is attached.
|
|
*
|
|
* @return True if Driver Station is attached, false otherwise.
|
|
*/
|
|
public static boolean isDSAttached() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getDSAttached();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets if a new control packet from the driver station arrived since the last time this function
|
|
* was called.
|
|
*
|
|
* @return True if the control data has been updated since the last call.
|
|
*/
|
|
public static boolean isNewControlData() {
|
|
m_waitForDataMutex.lock();
|
|
try {
|
|
int currentCount = m_waitForDataCount;
|
|
if (m_lastCount.get() != currentCount) {
|
|
m_lastCount.set(currentCount);
|
|
return true;
|
|
}
|
|
} finally {
|
|
m_waitForDataMutex.unlock();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets if the driver station attached to a Field Management System.
|
|
*
|
|
* @return true if the robot is competing on a field being controlled by a Field Management System
|
|
*/
|
|
public static boolean isFMSAttached() {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(false);
|
|
return m_controlWordCache.getFMSAttached();
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the game specific message.
|
|
*
|
|
* @return the game specific message
|
|
*/
|
|
public static String getGameSpecificMessage() {
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_matchInfo.gameSpecificMessage;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the event name.
|
|
*
|
|
* @return the event name
|
|
*/
|
|
public static String getEventName() {
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_matchInfo.eventName;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the match type.
|
|
*
|
|
* @return the match type
|
|
*/
|
|
public static MatchType getMatchType() {
|
|
int matchType;
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
matchType = m_matchInfo.matchType;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
switch (matchType) {
|
|
case 1:
|
|
return MatchType.Practice;
|
|
case 2:
|
|
return MatchType.Qualification;
|
|
case 3:
|
|
return MatchType.Elimination;
|
|
default:
|
|
return MatchType.None;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the match number.
|
|
*
|
|
* @return the match number
|
|
*/
|
|
public static int getMatchNumber() {
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_matchInfo.matchNumber;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the replay number.
|
|
*
|
|
* @return the replay number
|
|
*/
|
|
public static int getReplayNumber() {
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
return m_matchInfo.replayNumber;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the current alliance from the FMS.
|
|
*
|
|
* @return the current alliance
|
|
*/
|
|
public static Alliance getAlliance() {
|
|
AllianceStationID allianceStationID = HAL.getAllianceStation();
|
|
if (allianceStationID == null) {
|
|
return Alliance.Invalid;
|
|
}
|
|
|
|
switch (allianceStationID) {
|
|
case Red1:
|
|
case Red2:
|
|
case Red3:
|
|
return Alliance.Red;
|
|
|
|
case Blue1:
|
|
case Blue2:
|
|
case Blue3:
|
|
return Alliance.Blue;
|
|
|
|
default:
|
|
return Alliance.Invalid;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the location of the team's driver station controls.
|
|
*
|
|
* @return the location of the team's driver station controls: 1, 2, or 3
|
|
*/
|
|
public static int getLocation() {
|
|
AllianceStationID allianceStationID = HAL.getAllianceStation();
|
|
if (allianceStationID == null) {
|
|
return 0;
|
|
}
|
|
switch (allianceStationID) {
|
|
case Red1:
|
|
case Blue1:
|
|
return 1;
|
|
|
|
case Red2:
|
|
case Blue2:
|
|
return 2;
|
|
|
|
case Blue3:
|
|
case Red3:
|
|
return 3;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for new data from the driver station.
|
|
*
|
|
* <p>Checks if new control data has arrived since the last waitForData call on the current
|
|
* thread. If new data has not arrived, returns immediately.
|
|
*/
|
|
public static void waitForData() {
|
|
waitForData(0);
|
|
}
|
|
|
|
/**
|
|
* Wait for new data or for timeout, which ever comes first. If timeout is 0, wait for new data
|
|
* only.
|
|
*
|
|
* <p>Checks if new control data has arrived since the last waitForData call on the current
|
|
* thread. If new data has not arrived, returns immediately.
|
|
*
|
|
* @param timeoutSeconds The maximum time in seconds to wait.
|
|
* @return true if there is new data, otherwise false
|
|
*/
|
|
public static boolean waitForData(double timeoutSeconds) {
|
|
long startTimeMicroS = RobotController.getFPGATime();
|
|
long timeoutMicroS = (long) (timeoutSeconds * 1e6);
|
|
m_waitForDataMutex.lock();
|
|
try {
|
|
int currentCount = m_waitForDataCount;
|
|
if (m_lastCount.get() != currentCount) {
|
|
m_lastCount.set(currentCount);
|
|
return true;
|
|
}
|
|
while (m_waitForDataCount == currentCount) {
|
|
if (timeoutMicroS > 0) {
|
|
long nowMicroS = RobotController.getFPGATime();
|
|
if (nowMicroS < startTimeMicroS + timeoutMicroS) {
|
|
// We still have time to wait
|
|
boolean signaled =
|
|
m_waitForDataCond.await(
|
|
startTimeMicroS + timeoutMicroS - nowMicroS, TimeUnit.MICROSECONDS);
|
|
if (!signaled) {
|
|
// Return false if a timeout happened
|
|
return false;
|
|
}
|
|
} else {
|
|
// Time has elapsed.
|
|
return false;
|
|
}
|
|
} else {
|
|
m_waitForDataCond.await();
|
|
}
|
|
}
|
|
m_lastCount.set(m_waitForDataCount);
|
|
// Return true if we have received a proper signal
|
|
return true;
|
|
} catch (InterruptedException ex) {
|
|
// return false on a thread interrupt
|
|
Thread.currentThread().interrupt();
|
|
return false;
|
|
} finally {
|
|
m_waitForDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the approximate match time. The FMS does not send an official match time to the robots,
|
|
* but does send an approximate match time. The value will count down the time remaining in the
|
|
* current period (auto or teleop). Warning: This is not an official time (so it cannot be used to
|
|
* dispute ref calls or guarantee that a function will trigger before the match ends) The Practice
|
|
* Match function of the DS approximates the behavior seen on the field.
|
|
*
|
|
* @return Time remaining in current match period (auto or teleop) in seconds
|
|
*/
|
|
public static double getMatchTime() {
|
|
return HAL.getMatchTime();
|
|
}
|
|
|
|
/**
|
|
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
|
* purposes only.
|
|
*
|
|
* @param entering If true, starting disabled code; if false, leaving disabled code
|
|
*/
|
|
public static void inDisabled(boolean entering) {
|
|
m_userInDisabled = entering;
|
|
}
|
|
|
|
/**
|
|
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
|
* purposes only.
|
|
*
|
|
* @param entering If true, starting autonomous code; if false, leaving autonomous code
|
|
*/
|
|
public static void inAutonomous(boolean entering) {
|
|
m_userInAutonomous = entering;
|
|
}
|
|
|
|
/**
|
|
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
|
* purposes only.
|
|
*
|
|
* @param entering If true, starting teleop code; if false, leaving teleop code
|
|
* @deprecated Use {@link #inTeleop(boolean)} instead.
|
|
*/
|
|
@Deprecated(since = "2022", forRemoval = true)
|
|
public static void inOperatorControl(boolean entering) {
|
|
m_userInTeleop = entering;
|
|
}
|
|
|
|
/**
|
|
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
|
* purposes only.
|
|
*
|
|
* @param entering If true, starting teleop code; if false, leaving teleop code
|
|
*/
|
|
public static void inTeleop(boolean entering) {
|
|
m_userInTeleop = entering;
|
|
}
|
|
|
|
/**
|
|
* Only to be used to tell the Driver Station what code you claim to be executing for diagnostic
|
|
* purposes only.
|
|
*
|
|
* @param entering If true, starting test code; if false, leaving test code
|
|
*/
|
|
public static void inTest(boolean entering) {
|
|
m_userInTest = entering;
|
|
}
|
|
|
|
/** Forces waitForData() to return immediately. */
|
|
public static void wakeupWaitForData() {
|
|
m_waitForDataMutex.lock();
|
|
try {
|
|
m_waitForDataCount++;
|
|
m_waitForDataCond.signalAll();
|
|
} finally {
|
|
m_waitForDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allows the user to specify whether they want joystick connection warnings to be printed to the
|
|
* console. This setting is ignored when the FMS is connected -- warnings will always be on in
|
|
* that scenario.
|
|
*
|
|
* @param silence Whether warning messages should be silenced.
|
|
*/
|
|
public static void silenceJoystickConnectionWarning(boolean silence) {
|
|
m_silenceJoystickWarning = silence;
|
|
}
|
|
|
|
/**
|
|
* Returns whether joystick connection warnings are silenced. This will always return false when
|
|
* connected to the FMS.
|
|
*
|
|
* @return Whether joystick connection warnings are silenced.
|
|
*/
|
|
public static boolean isJoystickConnectionWarningSilenced() {
|
|
return !isFMSAttached() && m_silenceJoystickWarning;
|
|
}
|
|
|
|
/**
|
|
* Copy data from the DS task for the user. If no new data exists, it will just be returned,
|
|
* otherwise the data will be copied from the DS polling loop.
|
|
*/
|
|
protected static void getData() {
|
|
// Get the status of all of the joysticks
|
|
for (byte stick = 0; stick < kJoystickPorts; stick++) {
|
|
m_joystickAxesCache[stick].m_count =
|
|
HAL.getJoystickAxes(stick, m_joystickAxesCache[stick].m_axes);
|
|
m_joystickPOVsCache[stick].m_count =
|
|
HAL.getJoystickPOVs(stick, m_joystickPOVsCache[stick].m_povs);
|
|
m_joystickButtonsCache[stick].m_buttons = HAL.getJoystickButtons(stick, m_buttonCountBuffer);
|
|
m_joystickButtonsCache[stick].m_count = m_buttonCountBuffer.get(0);
|
|
}
|
|
|
|
HAL.getMatchInfo(m_matchInfoCache);
|
|
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
// Force a control word update, to make sure the data is the newest.
|
|
updateControlWord(true);
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
|
|
DataLogSender dataLogSender;
|
|
// lock joystick mutex to swap cache data
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
for (int i = 0; i < kJoystickPorts; i++) {
|
|
// If buttons weren't pressed and are now, set flags in m_buttonsPressed
|
|
m_joystickButtonsPressed[i] |=
|
|
~m_joystickButtons[i].m_buttons & m_joystickButtonsCache[i].m_buttons;
|
|
|
|
// If buttons were pressed and aren't now, set flags in m_buttonsReleased
|
|
m_joystickButtonsReleased[i] |=
|
|
m_joystickButtons[i].m_buttons & ~m_joystickButtonsCache[i].m_buttons;
|
|
}
|
|
|
|
// move cache to actual data
|
|
HALJoystickAxes[] currentAxes = m_joystickAxes;
|
|
m_joystickAxes = m_joystickAxesCache;
|
|
m_joystickAxesCache = currentAxes;
|
|
|
|
HALJoystickButtons[] currentButtons = m_joystickButtons;
|
|
m_joystickButtons = m_joystickButtonsCache;
|
|
m_joystickButtonsCache = currentButtons;
|
|
|
|
HALJoystickPOVs[] currentPOVs = m_joystickPOVs;
|
|
m_joystickPOVs = m_joystickPOVsCache;
|
|
m_joystickPOVsCache = currentPOVs;
|
|
|
|
MatchInfoData currentInfo = m_matchInfo;
|
|
m_matchInfo = m_matchInfoCache;
|
|
m_matchInfoCache = currentInfo;
|
|
|
|
dataLogSender = m_dataLogSender;
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
|
|
wakeupWaitForData();
|
|
m_matchDataSender.sendMatchData();
|
|
if (dataLogSender != null) {
|
|
dataLogSender.send(WPIUtilJNI.now());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm
|
|
* the DS.
|
|
*/
|
|
private static void reportJoystickUnpluggedError(String message) {
|
|
double currentTime = Timer.getFPGATimestamp();
|
|
if (currentTime > m_nextMessageTime) {
|
|
reportError(message, false);
|
|
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reports errors related to unplugged joysticks Throttles the errors so that they don't overwhelm
|
|
* the DS.
|
|
*/
|
|
private static void reportJoystickUnpluggedWarning(String message) {
|
|
if (isFMSAttached() || !m_silenceJoystickWarning) {
|
|
double currentTime = Timer.getFPGATimestamp();
|
|
if (currentTime > m_nextMessageTime) {
|
|
reportWarning(message, false);
|
|
m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Provides the service routine for the DS polling m_thread. */
|
|
private static void run() {
|
|
int safetyCounter = 0;
|
|
while (m_threadKeepAlive) {
|
|
HAL.waitForDSData();
|
|
getData();
|
|
|
|
if (isDisabled()) {
|
|
safetyCounter = 0;
|
|
}
|
|
|
|
safetyCounter++;
|
|
if (safetyCounter >= 4) {
|
|
MotorSafety.checkMotors();
|
|
safetyCounter = 0;
|
|
}
|
|
if (m_userInDisabled) {
|
|
HAL.observeUserProgramDisabled();
|
|
}
|
|
if (m_userInAutonomous) {
|
|
HAL.observeUserProgramAutonomous();
|
|
}
|
|
if (m_userInTeleop) {
|
|
HAL.observeUserProgramTeleop();
|
|
}
|
|
if (m_userInTest) {
|
|
HAL.observeUserProgramTest();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Forces a control word cache update, and update the passed in control word.
|
|
*
|
|
* @param word Word to update.
|
|
*/
|
|
public static void updateControlWordFromCache(ControlWord word) {
|
|
m_controlWordMutex.lock();
|
|
try {
|
|
updateControlWord(true);
|
|
word.update(m_controlWordCache);
|
|
} finally {
|
|
m_controlWordMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the data in the control word cache. Updates if the force parameter is set, or if 50ms
|
|
* have passed since the last update.
|
|
*
|
|
* <p>Must be called with m_controlWordMutex lock held.
|
|
*
|
|
* @param force True to force an update to the cache, otherwise update if 50ms have passed.
|
|
*/
|
|
private static void updateControlWord(boolean force) {
|
|
long now = System.currentTimeMillis();
|
|
if (now - m_lastControlWordUpdate > 50 || force) {
|
|
HAL.getControlWord(m_controlWordCache);
|
|
m_lastControlWordUpdate = now;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts logging DriverStation data to data log. Repeated calls are ignored.
|
|
*
|
|
* @param log data log
|
|
* @param logJoysticks if true, log joystick data
|
|
*/
|
|
@SuppressWarnings("PMD.NonThreadSafeSingleton")
|
|
public static void startDataLog(DataLog log, boolean logJoysticks) {
|
|
m_cacheDataMutex.lock();
|
|
try {
|
|
if (m_dataLogSender == null) {
|
|
m_dataLogSender = new DataLogSender(log, logJoysticks, WPIUtilJNI.now());
|
|
}
|
|
} finally {
|
|
m_cacheDataMutex.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts logging DriverStation data to data log, including joystick data. Repeated calls are
|
|
* ignored.
|
|
*
|
|
* @param log data log
|
|
*/
|
|
public static void startDataLog(DataLog log) {
|
|
startDataLog(log, true);
|
|
}
|
|
}
|