From a4f0c4fbe007cc1e0cbad67d95642ef85ba0efb2 Mon Sep 17 00:00:00 2001 From: Thad House Date: Sat, 21 May 2016 00:41:15 -0700 Subject: [PATCH] Implements locking in C++ DriverStation, and adds double buffering to DS Thread (#25) Currently, about 5ms of every 20ms loop the DS thread would hold the mutex, while grabbing data. During this time, and call to grab joystick data would be blocked. This change grabs the joystick data to a cache, and then grabs the mutex and moves the data references around. This is much more efficient then the old code, and gives teams more of their teleop loop time back for use. Another major change this does is use preallocated arrays when entering the JNI. Previously every JNI DS call would allocate an entire new array. With a GC'd language where those arrays go on the heap, thats a problem, and creates tons of garbage. That garbage is no longer created anymore, as all arrays and byte buffers sent to JNI in the DS are preallocated. In addition, GetJoystickName was always returning joystick 0 data, which this fixes. --- wpilibc/Athena/include/DriverStation.h | 17 +- wpilibc/Athena/src/DriverStation.cpp | 82 ++++-- .../lib/FRCNetworkCommunicationsLibrary.cpp | 30 +- .../edu/wpi/first/wpilibj/DriverStation.java | 270 ++++++++++++------ .../FRCNetworkCommunicationsLibrary.java | 6 +- 5 files changed, 274 insertions(+), 131 deletions(-) diff --git a/wpilibc/Athena/include/DriverStation.h b/wpilibc/Athena/include/DriverStation.h index 0fc2b886fa..3f310a3a09 100644 --- a/wpilibc/Athena/include/DriverStation.h +++ b/wpilibc/Athena/include/DriverStation.h @@ -9,6 +9,7 @@ #include #include +#include #include "HAL/HAL.hpp" #include "HAL/cpp/Semaphore.hpp" #include "HAL/cpp/priority_condition_variable.h" @@ -100,10 +101,17 @@ class DriverStation : public SensorBase, public RobotStateInterface { void ReportJoystickUnpluggedWarning(std::string message); void Run(); - HALJoystickAxes m_joystickAxes[kJoystickPorts]; - HALJoystickPOVs m_joystickPOVs[kJoystickPorts]; - HALJoystickButtons m_joystickButtons[kJoystickPorts]; - HALJoystickDescriptor m_joystickDescriptor[kJoystickPorts]; + std::unique_ptr m_joystickAxes; + std::unique_ptr m_joystickPOVs; + std::unique_ptr m_joystickButtons; + std::unique_ptr m_joystickDescriptor; + + // Cached Data + std::unique_ptr m_joystickAxesCache; + std::unique_ptr m_joystickPOVsCache; + std::unique_ptr m_joystickButtonsCache; + std::unique_ptr m_joystickDescriptorCache; + Task m_task; std::atomic m_isRunning{false}; mutable Semaphore m_newControlData{Semaphore::kEmpty}; @@ -111,6 +119,7 @@ class DriverStation : public SensorBase, public RobotStateInterface { priority_mutex m_packetDataAvailableMutex; std::condition_variable_any m_waitForDataCond; priority_mutex m_waitForDataMutex; + mutable priority_mutex m_joystickDataMutex; bool m_userInDisabled = false; bool m_userInAutonomous = false; bool m_userInTeleop = false; diff --git a/wpilibc/Athena/src/DriverStation.cpp b/wpilibc/Athena/src/DriverStation.cpp index 1d7e003b2c..474dce30bd 100644 --- a/wpilibc/Athena/src/DriverStation.cpp +++ b/wpilibc/Athena/src/DriverStation.cpp @@ -33,6 +33,15 @@ const uint32_t DriverStation::kJoystickPorts; * This is only called once the first time GetInstance() is called */ DriverStation::DriverStation() { + m_joystickAxes = std::make_unique (kJoystickPorts); + m_joystickPOVs = std::make_unique (kJoystickPorts); + m_joystickButtons = std::make_unique (kJoystickPorts); + m_joystickDescriptor = std::make_unique (kJoystickPorts); + m_joystickAxesCache = std::make_unique (kJoystickPorts); + m_joystickPOVsCache = std::make_unique (kJoystickPorts); + m_joystickButtonsCache = std::make_unique (kJoystickPorts); + m_joystickDescriptorCache = std::make_unique (kJoystickPorts); + // All joysticks should default to having zero axes, povs and buttons, so // uninitialized memory doesn't get sent to speed controllers. for (unsigned int i = 0; i < kJoystickPorts; i++) { @@ -42,6 +51,13 @@ DriverStation::DriverStation() { m_joystickDescriptor[i].isXbox = 0; m_joystickDescriptor[i].type = -1; m_joystickDescriptor[i].name[0] = '\0'; + + m_joystickAxesCache[i].count = 0; + m_joystickPOVsCache[i].count = 0; + m_joystickButtonsCache[i].count = 0; + m_joystickDescriptorCache[i].isXbox = 0; + m_joystickDescriptorCache[i].type = -1; + m_joystickDescriptorCache[i].name[0] = '\0'; } // Register that semaphore with the network communications task. // It will signal when new packet data is available. @@ -100,13 +116,21 @@ DriverStation& DriverStation::GetInstance() { * the data will be copied from the DS polling loop. */ void DriverStation::GetData() { - // Get the status of all of the joysticks + // Get the status of all of the joysticks, and save to the cache for (uint8_t stick = 0; stick < kJoystickPorts; stick++) { - HALGetJoystickAxes(stick, &m_joystickAxes[stick]); - HALGetJoystickPOVs(stick, &m_joystickPOVs[stick]); - HALGetJoystickButtons(stick, &m_joystickButtons[stick]); - HALGetJoystickDescriptor(stick, &m_joystickDescriptor[stick]); + HALGetJoystickAxes(stick, &m_joystickAxesCache[stick]); + HALGetJoystickPOVs(stick, &m_joystickPOVsCache[stick]); + HALGetJoystickButtons(stick, &m_joystickButtonsCache[stick]); + HALGetJoystickDescriptor(stick, &m_joystickDescriptorCache[stick]); } + // Obtain a write lock on the data, swap the cached data into the + // main data arrays + std::lock_guard lock(m_joystickDataMutex); + m_joystickAxes.swap(m_joystickAxesCache); + m_joystickPOVs.swap(m_joystickPOVsCache); + m_joystickButtons.swap(m_joystickButtonsCache); + m_joystickDescriptor.swap(m_joystickDescriptorCache); + m_newControlData.give(); } @@ -159,9 +183,8 @@ int DriverStation::GetStickAxisCount(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return 0; } - HALJoystickAxes joystickAxes; - HALGetJoystickAxes(stick, &joystickAxes); - return joystickAxes.count; + std::lock_guard lock(m_joystickDataMutex); + return m_joystickAxes[stick].count; } /** @@ -174,6 +197,7 @@ std::string DriverStation::GetJoystickName(uint32_t stick) const { if (stick >= kJoystickPorts) { wpi_setWPIError(BadJoystickIndex); } + std::lock_guard lock(m_joystickDataMutex); std::string retVal(m_joystickDescriptor[stick].name); return retVal; } @@ -189,6 +213,7 @@ int DriverStation::GetJoystickType(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return -1; } + std::lock_guard lock(m_joystickDataMutex); return (int)m_joystickDescriptor[stick].type; } @@ -203,6 +228,7 @@ bool DriverStation::GetJoystickIsXbox(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return false; } + std::lock_guard lock(m_joystickDataMutex); return (bool)m_joystickDescriptor[stick].isXbox; } @@ -217,6 +243,7 @@ int DriverStation::GetJoystickAxisType(uint32_t stick, uint8_t axis) const { wpi_setWPIError(BadJoystickIndex); return -1; } + std::lock_guard lock(m_joystickDataMutex); return m_joystickDescriptor[stick].axisTypes[axis]; } @@ -231,9 +258,8 @@ int DriverStation::GetStickPOVCount(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return 0; } - HALJoystickPOVs joystickPOVs; - HALGetJoystickPOVs(stick, &joystickPOVs); - return joystickPOVs.count; + std::lock_guard lock(m_joystickDataMutex); + return m_joystickPOVs[stick].count; } /** @@ -247,9 +273,8 @@ int DriverStation::GetStickButtonCount(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return 0; } - HALJoystickButtons joystickButtons; - HALGetJoystickButtons(stick, &joystickButtons); - return joystickButtons.count; + std::lock_guard lock(m_joystickDataMutex); + return m_joystickButtons[stick].count; } /** @@ -266,8 +291,11 @@ float DriverStation::GetStickAxis(uint32_t stick, uint32_t axis) { wpi_setWPIError(BadJoystickIndex); return 0; } - + std::unique_lock lock(m_joystickDataMutex); if (axis >= m_joystickAxes[stick].count) { + // Unlock early so error printing isn't locked. + m_joystickDataMutex.unlock(); + lock.release(); if (axis >= kMaxJoystickAxes) wpi_setWPIError(BadJoystickAxis); else @@ -295,8 +323,10 @@ int DriverStation::GetStickPOV(uint32_t stick, uint32_t pov) { wpi_setWPIError(BadJoystickIndex); return -1; } - + std::unique_lock lock(m_joystickDataMutex); if (pov >= m_joystickPOVs[stick].count) { + // Unlock early so error printing isn't locked. + lock.unlock(); if (pov >= kMaxJoystickPOVs) wpi_setWPIError(BadJoystickAxis); else @@ -319,7 +349,7 @@ uint32_t DriverStation::GetStickButtons(uint32_t stick) const { wpi_setWPIError(BadJoystickIndex); return 0; } - + std::lock_guard lock(m_joystickDataMutex); return m_joystickButtons[stick].buttons; } @@ -335,17 +365,21 @@ bool DriverStation::GetStickButton(uint32_t stick, uint8_t button) { wpi_setWPIError(BadJoystickIndex); return false; } - - if (button > m_joystickButtons[stick].count) { - ReportJoystickUnpluggedWarning( - "Joystick Button missing, check if all controllers are plugged in"); - return false; - } if (button == 0) { ReportJoystickUnpluggedError( - "Button indexes begin at 1 in WPILib for C++ and Java"); + "ERROR: Button indexes begin at 1 in WPILib for C++ and Java"); return false; } + std::unique_lock lock(m_joystickDataMutex); + if (button > m_joystickButtons[stick].count) { + // Unlock early so error printing isn't locked. + lock.unlock(); + ReportJoystickUnpluggedWarning( + "Joystick Button missing, check if all controllers are " + "plugged in"); + return false; + } + return ((0x1 << (button - 1)) & m_joystickButtons[stick].buttons) != 0; } diff --git a/wpilibj/src/athena/cpp/lib/FRCNetworkCommunicationsLibrary.cpp b/wpilibj/src/athena/cpp/lib/FRCNetworkCommunicationsLibrary.cpp index 6e3bb0cf6f..4c403f1a67 100644 --- a/wpilibj/src/athena/cpp/lib/FRCNetworkCommunicationsLibrary.cpp +++ b/wpilibj/src/athena/cpp/lib/FRCNetworkCommunicationsLibrary.cpp @@ -142,37 +142,47 @@ Java_edu_wpi_first_wpilibj_communication_FRCNetworkCommunicationsLibrary_NativeH /* * Class: edu_wpi_first_wpilibj_communication_FRCNetworkCommunicationsLibrary * Method: HALGetJoystickAxes - * Signature: (B)[S + * Signature: (B[S)B */ -JNIEXPORT jshortArray JNICALL +JNIEXPORT jbyte JNICALL Java_edu_wpi_first_wpilibj_communication_FRCNetworkCommunicationsLibrary_HALGetJoystickAxes( - JNIEnv *env, jclass, jbyte joystickNum) { + JNIEnv * env, jclass, jbyte joystickNum, jshortArray axesArray) { NETCOMM_LOG(logDEBUG) << "Calling HALJoystickAxes"; HALJoystickAxes axes; HALGetJoystickAxes(joystickNum, &axes); - jshortArray axesArray = env->NewShortArray(axes.count); + jsize javaSize = env->GetArrayLength(axesArray); + if (axes.count > javaSize) + { + ThrowIllegalArgumentException(env, "Native array size larger then passed in java array size"); + } + env->SetShortArrayRegion(axesArray, 0, axes.count, axes.axes); - return axesArray; + return axes.count; } /* * Class: edu_wpi_first_wpilibj_communication_FRCNetworkCommunicationsLibrary * Method: HALGetJoystickPOVs - * Signature: (B)[S + * Signature: (B[S)B */ -JNIEXPORT jshortArray JNICALL +JNIEXPORT jbyte JNICALL Java_edu_wpi_first_wpilibj_communication_FRCNetworkCommunicationsLibrary_HALGetJoystickPOVs( - JNIEnv *env, jclass, jbyte joystickNum) { + JNIEnv * env, jclass, jbyte joystickNum, jshortArray povsArray) { NETCOMM_LOG(logDEBUG) << "Calling HALJoystickPOVs"; HALJoystickPOVs povs; HALGetJoystickPOVs(joystickNum, &povs); - jshortArray povsArray = env->NewShortArray(povs.count); + jsize javaSize = env->GetArrayLength(povsArray); + if (povs.count > javaSize) + { + ThrowIllegalArgumentException(env, "Native array size larger then passed in java array size"); + } + env->SetShortArrayRegion(povsArray, 0, povs.count, povs.povs); - return povsArray; + return povs.count; } /* diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DriverStation.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DriverStation.java index 7f95b5d738..530066c4a5 100644 --- a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DriverStation.java +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/DriverStation.java @@ -30,6 +30,24 @@ public class DriverStation implements RobotState.Interface { public byte m_count; } + private class HALJoystickAxes { + public short[] m_axes; + public byte m_count; + + public HALJoystickAxes(int count) { + m_axes = new short[count]; + } + } + + private class HALJoystickPOVs { + public short[] m_povs; + public byte m_count; + + public HALJoystickPOVs(int count) { + m_povs = new short[count]; + } + } + /** * The robot alliance that the robot is a part of. */ @@ -55,11 +73,16 @@ public class DriverStation implements RobotState.Interface { private static DriverStation instance = new DriverStation(); - private short[][] m_joystickAxes = - new short[kJoystickPorts][FRCNetworkCommunicationsLibrary.kMaxJoystickAxes]; - private short[][] m_joystickPOVs = - new short[kJoystickPorts][FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs]; + private HALJoystickAxes[] m_joystickAxes = new HALJoystickAxes[kJoystickPorts]; + private HALJoystickPOVs[] m_joystickPOVs = new HALJoystickPOVs[kJoystickPorts]; private HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[kJoystickPorts]; + + private HALJoystickAxes[] m_joystickAxesCache = new HALJoystickAxes[kJoystickPorts]; + private HALJoystickPOVs[] m_joystickPOVsCache = new HALJoystickPOVs[kJoystickPorts]; + private HALJoystickButtons[] m_joystickButtonsCache = new HALJoystickButtons[kJoystickPorts]; + // preallocated byte buffer for button count + private ByteBuffer m_buttonCountBuffer = ByteBuffer.allocateDirect(1); + private int[] m_joystickIsXbox = new int[kJoystickPorts]; private int[] m_joystickType = new int[kJoystickPorts]; private String[] m_joystickName = new String[kJoystickPorts]; @@ -68,7 +91,10 @@ public class DriverStation implements RobotState.Interface { private Thread m_thread; private final Object m_dataSem; + private final Object m_newControlDataMutex; + private final Object m_joystickMutex; private volatile boolean m_threadKeepAlive = true; + private boolean m_userInDisabled = false; private boolean m_userInAutonomous = false; private boolean m_userInTeleop = false; @@ -94,8 +120,18 @@ public class DriverStation implements RobotState.Interface { */ protected DriverStation() { m_dataSem = new Object(); + m_joystickMutex = new Object(); + m_newControlDataMutex = new Object(); for (int i = 0; i < kJoystickPorts; i++) { m_joystickButtons[i] = new HALJoystickButtons(); + m_joystickAxes[i] = new HALJoystickAxes(FRCNetworkCommunicationsLibrary.kMaxJoystickAxes); + m_joystickPOVs[i] = new HALJoystickPOVs(FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs); + + m_joystickButtonsCache[i] = new HALJoystickButtons(); + m_joystickAxesCache[i] = + new HALJoystickAxes(FRCNetworkCommunicationsLibrary.kMaxJoystickAxes); + m_joystickPOVsCache[i] = + new HALJoystickPOVs(FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs); } m_packetDataAvailableMutex = HALUtil.initializeMutexNormal(); @@ -122,9 +158,7 @@ public class DriverStation implements RobotState.Interface { int safetyCounter = 0; while (m_threadKeepAlive) { HALUtil.takeMultiWait(m_packetDataAvailableSem, m_packetDataAvailableMutex); - synchronized (this) { - getData(); - } + getData(); synchronized (m_dataSem) { m_dataSem.notifyAll(); } @@ -174,19 +208,38 @@ public class DriverStation implements RobotState.Interface { * 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 synchronized void getData() { - + protected void getData() { // Get the status of all of the joysticks for (byte stick = 0; stick < kJoystickPorts; stick++) { - m_joystickAxes[stick] = FRCNetworkCommunicationsLibrary.HALGetJoystickAxes(stick); - m_joystickPOVs[stick] = FRCNetworkCommunicationsLibrary.HALGetJoystickPOVs(stick); - ByteBuffer countBuffer = ByteBuffer.allocateDirect(1); - m_joystickButtons[stick].m_buttons = - FRCNetworkCommunicationsLibrary.HALGetJoystickButtons(stick, countBuffer); - m_joystickButtons[stick].m_count = countBuffer.get(); + m_joystickAxesCache[stick].m_count = + FRCNetworkCommunicationsLibrary.HALGetJoystickAxes(stick, + m_joystickAxesCache[stick].m_axes); + m_joystickPOVsCache[stick].m_count = + FRCNetworkCommunicationsLibrary.HALGetJoystickPOVs(stick, + m_joystickPOVsCache[stick].m_povs); + m_joystickButtonsCache[stick].m_buttons = + FRCNetworkCommunicationsLibrary.HALGetJoystickButtons(stick, m_buttonCountBuffer); + m_joystickButtonsCache[stick].m_count = m_buttonCountBuffer.get(0); } + // lock joystick mutex to swap cache data + synchronized (m_joystickMutex) { + // move cache to actual data + HALJoystickAxes[] currentAxes = m_joystickAxes; + m_joystickAxes = m_joystickAxesCache; + m_joystickAxesCache = currentAxes; - m_newControlData = true; + HALJoystickButtons[] currentButtons = m_joystickButtons; + m_joystickButtons = m_joystickButtonsCache; + m_joystickButtonsCache = currentButtons; + + HALJoystickPOVs[] currentPOVs = m_joystickPOVs; + m_joystickPOVs = m_joystickPOVsCache; + m_joystickPOVsCache = currentPOVs; + } + //Lock new control data mutex and set new control data. + synchronized (m_newControlDataMutex) { + m_newControlData = true; + } } /** @@ -230,28 +283,30 @@ public class DriverStation implements RobotState.Interface { * @param axis The analog axis value to read from the joystick. * @return The value of the axis on the joystick. */ - public synchronized double getStickAxis(int stick, int axis) { + public double getStickAxis(int stick, int axis) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - if (axis < 0 || axis >= FRCNetworkCommunicationsLibrary.kMaxJoystickAxes) { throw new RuntimeException("Joystick axis is out of range"); } - - if (axis >= m_joystickAxes[stick].length) { + + boolean error = false; + double retVal = 0.0; + synchronized (m_joystickMutex) { + if (axis >= m_joystickAxes[stick].m_count) { + // set error + error = true; + retVal = 0.0; + } else { + retVal = m_joystickAxes[stick].m_axes[axis]; + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick axis " + axis + " on port " + stick + " not available, check if controller is plugged in"); - return 0.0; - } - - byte value = (byte) m_joystickAxes[stick][axis]; - - if (value < 0) { - return value / 128.0; - } else { - return value / 127.0; } + return retVal; } /** @@ -260,13 +315,13 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return The number of axes on the indicated joystick */ - public synchronized int getStickAxisCount(int stick) { - + public int getStickAxisCount(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - - return m_joystickAxes[stick].length; + synchronized (m_joystickMutex) { + return m_joystickAxes[stick].m_count; + } } /** @@ -274,22 +329,28 @@ public class DriverStation implements RobotState.Interface { * * @return the angle of the POV in degrees, or -1 if the POV is not pressed. */ - public synchronized int getStickPOV(int stick, int pov) { + public int getStickPOV(int stick, int pov) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - if (pov < 0 || pov >= FRCNetworkCommunicationsLibrary.kMaxJoystickPOVs) { throw new RuntimeException("Joystick POV is out of range"); } - - if (pov >= m_joystickPOVs[stick].length) { + boolean error = false; + int retVal = -1; + synchronized (m_joystickMutex) { + if (pov >= m_joystickPOVs[stick].m_count) { + error = true; + retVal = -1; + } else { + retVal = m_joystickPOVs[stick].m_povs[pov]; + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick POV " + pov + " on port " + stick + " not available, check if controller is plugged in"); - return -1; } - - return m_joystickPOVs[stick][pov]; + return retVal; } /** @@ -298,13 +359,13 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return The number of POVs on the indicated joystick */ - public synchronized int getStickPOVCount(int stick) { - + public int getStickPOVCount(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - - return m_joystickPOVs[stick].length; + synchronized (m_joystickMutex) { + return m_joystickPOVs[stick].m_count; + } } /** @@ -313,12 +374,13 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick to read. * @return The state of the buttons on the joystick. */ - public synchronized int getStickButtons(final int stick) { + public int getStickButtons(final int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-3"); } - - return m_joystickButtons[stick].m_buttons; + synchronized (m_joystickMutex) { + return m_joystickButtons[stick].m_buttons; + } } /** @@ -328,22 +390,29 @@ public class DriverStation implements RobotState.Interface { * @param button The button index, beginning at 1. * @return The state of the joystick button. */ - public synchronized boolean getStickButton(final int stick, byte button) { + public boolean getStickButton(final int stick, byte 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 RuntimeException("Joystick index is out of range, should be 0-3"); } - - - if (button > m_joystickButtons[stick].m_count) { + boolean error = false; + boolean retVal = false; + synchronized (m_joystickMutex) { + if (button > m_joystickButtons[stick].m_count) { + error = true; + retVal = false; + } else { + retVal = ((0x1 << (button - 1)) & m_joystickButtons[stick].m_buttons) != 0; + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick Button " + button + " on port " + stick + " not available, check if controller is plugged in"); - return false; } - if (button <= 0) { - reportJoystickUnpluggedError("Button indexes begin at 1 in WPILib for C++ and Java"); - return false; - } - return ((0x1 << (button - 1)) & m_joystickButtons[stick].m_buttons) != 0; + return retVal; } /** @@ -352,14 +421,13 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return The number of buttons on the indicated joystick */ - public synchronized int getStickButtonCount(int stick) { - + public int getStickButtonCount(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - - - return m_joystickButtons[stick].m_count; + synchronized (m_joystickMutex) { + return m_joystickButtons[stick].m_count; + } } /** @@ -368,21 +436,25 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return A boolean that returns the value of isXbox */ - public synchronized boolean getJoystickIsXbox(int stick) { - + public boolean getJoystickIsXbox(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - // TODO: Remove this when calling for descriptor on empty stick no longer - // crashes - if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].length) { + boolean error = false; + boolean retVal = false; + synchronized (m_joystickMutex) { + // TODO: Remove this when calling for descriptor on empty stick no longer + // crashes + if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) { + error = true; + retVal = false; + } else if (FRCNetworkCommunicationsLibrary.HALGetJoystickIsXbox((byte) stick) == 1) { + retVal = true; + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick on port " + stick + " not available, check if controller is plugged in"); - return false; - } - boolean retVal = false; - if (FRCNetworkCommunicationsLibrary.HALGetJoystickIsXbox((byte) stick) == 1) { - retVal = true; } return retVal; } @@ -393,19 +465,27 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return The value of type */ - public synchronized int getJoystickType(int stick) { - + public int getJoystickType(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - // TODO: Remove this when calling for descriptor on empty stick no longer - // crashes - if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].length) { + boolean error = false; + int retVal = -1; + synchronized (m_joystickMutex) { + // TODO: Remove this when calling for descriptor on empty stick no longer + // crashes + if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) { + error = true; + retVal = -1; + } else { + retVal = FRCNetworkCommunicationsLibrary.HALGetJoystickType((byte) stick); + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick on port " + stick + " not available, check if controller is plugged in"); - return -1; } - return FRCNetworkCommunicationsLibrary.HALGetJoystickType((byte) stick); + return retVal; } /** @@ -414,19 +494,27 @@ public class DriverStation implements RobotState.Interface { * @param stick The joystick port number * @return The value of name */ - public synchronized String getJoystickName(int stick) { - + public String getJoystickName(int stick) { if (stick < 0 || stick >= kJoystickPorts) { throw new RuntimeException("Joystick index is out of range, should be 0-5"); } - // TODO: Remove this when calling for descriptor on empty stick no longer - // crashes - if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].length) { + boolean error = false; + String retVal = ""; + synchronized (m_joystickMutex) { + // TODO: Remove this when calling for descriptor on empty stick no longer + // crashes + if (1 > m_joystickButtons[stick].m_count && 1 > m_joystickAxes[stick].m_count) { + error = true; + retVal = ""; + } else { + retVal = FRCNetworkCommunicationsLibrary.HALGetJoystickName((byte) stick); + } + } + if (error) { reportJoystickUnpluggedWarning("Joystick on port " + stick + " not available, check if controller is plugged in"); - return ""; } - return FRCNetworkCommunicationsLibrary.HALGetJoystickName((byte) stick); + return retVal; } /** @@ -505,10 +593,12 @@ public class DriverStation implements RobotState.Interface { * * @return True if the control data has been updated since the last call. */ - public synchronized boolean isNewControlData() { - boolean result = m_newControlData; - m_newControlData = false; - return result; + public boolean isNewControlData() { + synchronized (m_newControlDataMutex) { + boolean result = m_newControlData; + m_newControlData = false; + return result; + } } /** diff --git a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java index c5e88a1d78..cb821efef8 100644 --- a/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java +++ b/wpilibj/src/athena/java/edu/wpi/first/wpilibj/communication/FRCNetworkCommunicationsLibrary.java @@ -224,10 +224,10 @@ public class FRCNetworkCommunicationsLibrary extends JNIWrapper { public static int kMaxJoystickAxes = 12; public static int kMaxJoystickPOVs = 12; + + public static native byte HALGetJoystickAxes(byte joystickNum, short[] axesArray); - public static native short[] HALGetJoystickAxes(byte joystickNum); - - public static native short[] HALGetJoystickPOVs(byte joystickNum); + public static native byte HALGetJoystickPOVs(byte joystickNum, short[] povsArray); public static native int HALGetJoystickButtons(byte joystickNum, ByteBuffer count);