From 4059e0cd9f4cfc768efa79a81cde0586844042b9 Mon Sep 17 00:00:00 2001 From: Ryan Blue Date: Fri, 22 Dec 2023 13:57:52 -0500 Subject: [PATCH] [hal,wpilib] Add function to control "Radio" LED (#6073) --- .../main/java/edu/wpi/first/hal/LEDJNI.java | 28 ++++++ .../first/hal/simulation/RoboRioDataJNI.java | 9 ++ hal/src/main/native/athena/HAL.cpp | 1 + hal/src/main/native/athena/HALInitializer.h | 1 + hal/src/main/native/athena/LEDs.cpp | 89 +++++++++++++++++++ .../native/athena/mockdata/RoboRioData.cpp | 1 + hal/src/main/native/cpp/jni/LEDJNI.cpp | 51 +++++++++++ .../cpp/jni/simulation/RoboRioDataJNI.cpp | 51 +++++++++++ hal/src/main/native/include/hal/HAL.h | 1 + hal/src/main/native/include/hal/LEDs.h | 30 +++++++ .../include/hal/simulation/RoboRioData.h | 13 ++- hal/src/main/native/sim/LEDs.cpp | 21 +++++ .../main/native/sim/mockdata/RoboRioData.cpp | 2 + .../native/sim/mockdata/RoboRioDataInternal.h | 7 ++ .../src/main/native/cpp/RobotController.cpp | 25 ++++++ .../main/native/cpp/simulation/RoboRioSim.cpp | 17 ++++ .../main/native/include/frc/RobotController.h | 19 ++++ .../include/frc/simulation/RoboRioSim.h | 27 ++++++ .../native/cpp/simulation/RoboRioSimTest.cpp | 22 +++++ .../wpi/first/wpilibj/RobotController.java | 61 +++++++++++++ .../first/wpilibj/simulation/RoboRioSim.java | 34 +++++++ .../wpilibj/simulation/RoboRioSimTest.java | 24 +++++ 22 files changed, 531 insertions(+), 3 deletions(-) create mode 100644 hal/src/main/java/edu/wpi/first/hal/LEDJNI.java create mode 100644 hal/src/main/native/athena/LEDs.cpp create mode 100644 hal/src/main/native/cpp/jni/LEDJNI.cpp create mode 100644 hal/src/main/native/include/hal/LEDs.h create mode 100644 hal/src/main/native/sim/LEDs.cpp diff --git a/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java b/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java new file mode 100644 index 00000000000..bdb4b02d39f --- /dev/null +++ b/hal/src/main/java/edu/wpi/first/hal/LEDJNI.java @@ -0,0 +1,28 @@ +// 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.hal; + +public class LEDJNI extends JNIWrapper { + public static final int RADIO_LED_STATE_OFF = 0; + public static final int RADIO_LED_STATE_GREEN = 1; + public static final int RADIO_LED_STATE_RED = 2; + public static final int RADIO_LED_STATE_ORANGE = 3; + + /** + * Set the state of the "Radio" LED. + * + * @param state The state to set the LED to. + * @see "HAL_SetRadioLEDState" + */ + public static native void setRadioLEDState(int state); + + /** + * Get the state of the "Radio" LED. + * + * @return The state of the LED. + * @see "HAL_GetRadioLEDState" + */ + public static native int getRadioLEDState(); +} diff --git a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java index 7f51331a799..f5362279163 100644 --- a/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/simulation/RoboRioDataJNI.java @@ -176,5 +176,14 @@ public static native int registerTeamNumberCallback( public static native void setComments(String comments); + public static native int registerRadioLEDStateCallback( + NotifyCallback callback, boolean initialNotify); + + public static native void cancelRadioLEDStateCallback(int uid); + + public static native int getRadioLEDState(); + + public static native void setRadioLEDState(int state); + public static native void resetData(); } diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index f8acb3322cd..f239391b360 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -83,6 +83,7 @@ void InitializeHAL() { InitializeFRCDriverStation(); InitializeI2C(); InitializeInterrupts(); + InitializeLEDs(); InitializeMain(); InitializeNotifier(); InitializeCTREPDP(); diff --git a/hal/src/main/native/athena/HALInitializer.h b/hal/src/main/native/athena/HALInitializer.h index 182ab5b9738..20b9a8bff08 100644 --- a/hal/src/main/native/athena/HALInitializer.h +++ b/hal/src/main/native/athena/HALInitializer.h @@ -40,6 +40,7 @@ extern void InitializeFRCDriverStation(); extern void InitializeHAL(); extern void InitializeI2C(); extern void InitializeInterrupts(); +extern void InitializeLEDs(); extern void InitializeMain(); extern void InitializeNotifier(); extern void InitializeCTREPDP(); diff --git a/hal/src/main/native/athena/LEDs.cpp b/hal/src/main/native/athena/LEDs.cpp new file mode 100644 index 00000000000..8fa603e0fa0 --- /dev/null +++ b/hal/src/main/native/athena/LEDs.cpp @@ -0,0 +1,89 @@ +// 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. + +#include "hal/LEDs.h" + +#include + +#include + +#include + +#include "hal/Errors.h" + +namespace hal::init { + +void InitializeLEDs() { + int32_t status = 0; + HAL_SetRadioLEDState(HAL_RadioLED_kOff, &status); +} +} // namespace hal::init + +static const fs::path radioLEDGreenFilePath = + "/sys/class/leds/nilrt:wifi:primary/brightness"; +static const fs::path radioLEDRedFilePath = + "/sys/class/leds/nilrt:wifi:secondary/brightness"; + +static const char* onStr = "1"; +static const char* offStr = "0"; + +extern "C" { +void HAL_SetRadioLEDState(HAL_RadioLEDState state, int32_t* status) { + std::error_code ec; + fs::file_t greenFile = fs::OpenFileForWrite(radioLEDGreenFilePath, ec, + fs::CD_OpenExisting, fs::OF_Text); + if (ec) { + *status = INCOMPATIBLE_STATE; + return; + } + fs::file_t redFile = fs::OpenFileForWrite(radioLEDRedFilePath, ec, + fs::CD_OpenExisting, fs::OF_Text); + if (ec) { + *status = INCOMPATIBLE_STATE; + return; + } + + write(greenFile, state & HAL_RadioLED_kGreen ? onStr : offStr, 1); + write(redFile, state & HAL_RadioLED_kRed ? onStr : offStr, 1); + + fs::CloseFile(greenFile); + fs::CloseFile(redFile); +} + +bool ReadStateFromFile(fs::path path, int32_t* status) { + std::error_code ec; + fs::file_t file = fs::OpenFileForRead(path, ec, fs::OF_Text); + if (ec) { + *status = INCOMPATIBLE_STATE; + return false; + } + // We only need to read one byte because the file won't have leading zeros. + char buf[1]{}; + size_t count = read(file, buf, 1); + if (count == 0) { + *status = INCOMPATIBLE_STATE; + return false; + } + // If the brightness is not zero, the LED is on. + return buf[0] != '0'; +} + +HAL_RadioLEDState HAL_GetRadioLEDState(int32_t* status) { + bool green = ReadStateFromFile(radioLEDGreenFilePath, status); + bool red = ReadStateFromFile(radioLEDRedFilePath, status); + if (*status == 0) { + if (green && red) { + return HAL_RadioLED_kOrange; + } else if (green) { + return HAL_RadioLED_kGreen; + } else if (red) { + return HAL_RadioLED_kRed; + } else { + return HAL_RadioLED_kOff; + } + } else { + return HAL_RadioLED_kOff; + } +} +} // extern "C" diff --git a/hal/src/main/native/athena/mockdata/RoboRioData.cpp b/hal/src/main/native/athena/mockdata/RoboRioData.cpp index 85593942b1e..b57b706b8ab 100644 --- a/hal/src/main/native/athena/mockdata/RoboRioData.cpp +++ b/hal/src/main/native/athena/mockdata/RoboRioData.cpp @@ -30,6 +30,7 @@ DEFINE_CAPI(int32_t, UserFaults3V3, 0) DEFINE_CAPI(double, BrownoutVoltage, 6.75) DEFINE_CAPI(double, CPUTemp, 45.0) DEFINE_CAPI(int32_t, TeamNumber, 0) +DEFINE_CAPI(HAL_RadioLEDState, RadioLEDState, HAL_RadioLED_kOff); int32_t HALSIM_RegisterRoboRioSerialNumberCallback( HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) { diff --git a/hal/src/main/native/cpp/jni/LEDJNI.cpp b/hal/src/main/native/cpp/jni/LEDJNI.cpp new file mode 100644 index 00000000000..62aeaffbf23 --- /dev/null +++ b/hal/src/main/native/cpp/jni/LEDJNI.cpp @@ -0,0 +1,51 @@ +// 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. + +#include + +#include "HALUtil.h" +#include "edu_wpi_first_hal_LEDJNI.h" +#include "hal/LEDs.h" + +static_assert(edu_wpi_first_hal_LEDJNI_RADIO_LED_STATE_OFF == + HAL_RadioLEDState::HAL_RadioLED_kOff); +static_assert(edu_wpi_first_hal_LEDJNI_RADIO_LED_STATE_GREEN == + HAL_RadioLEDState::HAL_RadioLED_kGreen); +static_assert(edu_wpi_first_hal_LEDJNI_RADIO_LED_STATE_RED == + HAL_RadioLEDState::HAL_RadioLED_kRed); +static_assert(edu_wpi_first_hal_LEDJNI_RADIO_LED_STATE_ORANGE == + HAL_RadioLEDState::HAL_RadioLED_kOrange); + +using namespace hal; + +extern "C" { +/* + * Class: edu_wpi_first_hal_LEDJNI + * Method: setRadioLEDState + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_LEDJNI_setRadioLEDState + (JNIEnv* env, jclass, jint state) +{ + int32_t status = 0; + HAL_SetRadioLEDState(static_cast(state), &status); + CheckStatus(env, status); +} + +/* + * Class: edu_wpi_first_hal_LEDJNI + * Method: getRadioLEDState + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_LEDJNI_getRadioLEDState + (JNIEnv* env, jclass) +{ + int32_t status = 0; + auto retVal = HAL_GetRadioLEDState(&status); + CheckStatus(env, status); + return retVal; +} +} // extern "C" diff --git a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp index 9555f593192..c9a2acaa673 100644 --- a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp +++ b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp @@ -983,6 +983,57 @@ Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setComments HALSIM_SetRoboRioComments(commentsJString.c_str(), commentsJString.size()); } +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: registerRadioLEDStateCallback + * Signature: (Ljava/lang/Object;Z)I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_registerRadioLEDStateCallback + (JNIEnv* env, jclass, jobject callback, jboolean initialNotify) +{ + return sim::AllocateCallbackNoIndex( + env, callback, initialNotify, + &HALSIM_RegisterRoboRioRadioLEDStateCallback); +} + +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: cancelRadioLEDStateCallback + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_cancelRadioLEDStateCallback + (JNIEnv* env, jclass, jint handle) +{ + return sim::FreeCallbackNoIndex(env, handle, + &HALSIM_CancelRoboRioRadioLEDStateCallback); +} + +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: getRadioLEDState + * Signature: ()I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getRadioLEDState + (JNIEnv*, jclass) +{ + return HALSIM_GetRoboRioRadioLEDState(); +} + +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: setRadioLEDState + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setRadioLEDState + (JNIEnv*, jclass, jint value) +{ + HALSIM_SetRoboRioRadioLEDState(static_cast(value)); +} + /* * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI * Method: resetData diff --git a/hal/src/main/native/include/hal/HAL.h b/hal/src/main/native/include/hal/HAL.h index 0d6023729bb..84bee9f75ad 100644 --- a/hal/src/main/native/include/hal/HAL.h +++ b/hal/src/main/native/include/hal/HAL.h @@ -25,6 +25,7 @@ #include "hal/HALBase.h" #include "hal/I2C.h" #include "hal/Interrupts.h" +#include "hal/LEDs.h" #include "hal/Main.h" #include "hal/Notifier.h" #include "hal/PWM.h" diff --git a/hal/src/main/native/include/hal/LEDs.h b/hal/src/main/native/include/hal/LEDs.h new file mode 100644 index 00000000000..4b247472644 --- /dev/null +++ b/hal/src/main/native/include/hal/LEDs.h @@ -0,0 +1,30 @@ +// 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. + +#pragma once +#include "hal/Types.h" + +HAL_ENUM(HAL_RadioLEDState){HAL_RadioLED_kOff = 0, HAL_RadioLED_kGreen = 1, + HAL_RadioLED_kRed = 2, HAL_RadioLED_kOrange = 3}; + +#ifdef __cplusplus +extern "C" { +#endif +/** + * Set the state of the "Radio" LED. + * @param state The state to set the LED to. + * @param[out] status the error code, or 0 for success + */ +void HAL_SetRadioLEDState(HAL_RadioLEDState state, int32_t* status); + +/** + * Get the state of the "Radio" LED. + * + * @param[out] status the error code, or 0 for success + * @return The state of the LED. + */ +HAL_RadioLEDState HAL_GetRadioLEDState(int32_t* status); +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/hal/src/main/native/include/hal/simulation/RoboRioData.h b/hal/src/main/native/include/hal/simulation/RoboRioData.h index d191fcb41f6..83d68eadebe 100644 --- a/hal/src/main/native/include/hal/simulation/RoboRioData.h +++ b/hal/src/main/native/include/hal/simulation/RoboRioData.h @@ -6,6 +6,7 @@ #include +#include "hal/LEDs.h" #include "hal/Types.h" #include "hal/simulation/NotifyListener.h" @@ -145,9 +146,6 @@ void HALSIM_CancelRoboRioCommentsCallback(int32_t uid); size_t HALSIM_GetRoboRioComments(char* buffer, size_t size); void HALSIM_SetRoboRioComments(const char* comments, size_t size); -void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback, - void* param, HAL_Bool initialNotify); - int32_t HALSIM_RegisterRoboRioCPUTempCallback(HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify); @@ -155,6 +153,15 @@ void HALSIM_CancelRoboRioCPUTempCallback(int32_t uid); double HALSIM_GetRoboRioCPUTemp(void); void HALSIM_SetRoboRioCPUTemp(double cpuTemp); +int32_t HALSIM_RegisterRoboRioRadioLEDStateCallback(HAL_NotifyCallback callback, + void* param, + HAL_Bool initialNotify); +void HALSIM_CancelRoboRioRadioLEDStateCallback(int32_t uid); +HAL_RadioLEDState HALSIM_GetRoboRioRadioLEDState(void); +void HALSIM_SetRoboRioRadioLEDState(HAL_RadioLEDState state); + +void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback, + void* param, HAL_Bool initialNotify); #ifdef __cplusplus } // extern "C" #endif diff --git a/hal/src/main/native/sim/LEDs.cpp b/hal/src/main/native/sim/LEDs.cpp new file mode 100644 index 00000000000..e7d45aa7f7d --- /dev/null +++ b/hal/src/main/native/sim/LEDs.cpp @@ -0,0 +1,21 @@ +// 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. + +#include "hal/LEDs.h" + +#include "hal/simulation/RoboRioData.h" + +namespace hal::init { +void InitializeLEDs() {} +} // namespace hal::init + +extern "C" { + +void HAL_SetRadioLEDState(HAL_RadioLEDState state, int32_t* status) { + HALSIM_SetRoboRioRadioLEDState(state); +} +HAL_RadioLEDState HAL_GetRadioLEDState(int32_t* status) { + return HALSIM_GetRoboRioRadioLEDState(); +} +} // extern "C" diff --git a/hal/src/main/native/sim/mockdata/RoboRioData.cpp b/hal/src/main/native/sim/mockdata/RoboRioData.cpp index 9a9a1a1d38e..8ccc019edb7 100644 --- a/hal/src/main/native/sim/mockdata/RoboRioData.cpp +++ b/hal/src/main/native/sim/mockdata/RoboRioData.cpp @@ -136,6 +136,7 @@ DEFINE_CAPI(int32_t, UserFaults3V3, userFaults3V3) DEFINE_CAPI(double, BrownoutVoltage, brownoutVoltage) DEFINE_CAPI(double, CPUTemp, cpuTemp) DEFINE_CAPI(int32_t, TeamNumber, teamNumber) +DEFINE_CAPI(HAL_RadioLEDState, RadioLEDState, radioLedState) int32_t HALSIM_RegisterRoboRioSerialNumberCallback( HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) { @@ -192,5 +193,6 @@ void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback, REGISTER(userFaults3V3); REGISTER(brownoutVoltage); REGISTER(cpuTemp); + REGISTER(radioLedState); } } // extern "C" diff --git a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h index aef61bea749..413c38eb2d3 100644 --- a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h +++ b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h @@ -32,10 +32,15 @@ class RoboRioData { HAL_SIMDATAVALUE_DEFINE_NAME(BrownoutVoltage) HAL_SIMDATAVALUE_DEFINE_NAME(CPUTemp) HAL_SIMDATAVALUE_DEFINE_NAME(TeamNumber) + HAL_SIMDATAVALUE_DEFINE_NAME(RadioLEDState) HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(SerialNumber) HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(Comments); + static inline HAL_Value MakeRadioLEDStateValue(HAL_RadioLEDState value) { + return HAL_MakeEnum(value); + } + public: SimDataValue fpgaButton{false}; SimDataValue vInVoltage{12.0}; @@ -61,6 +66,8 @@ class RoboRioData { 6.75}; SimDataValue cpuTemp{45.0}; SimDataValue teamNumber{0}; + SimDataValue + radioLedState{HAL_RadioLED_kOff}; int32_t RegisterSerialNumberCallback(HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify); diff --git a/wpilibc/src/main/native/cpp/RobotController.cpp b/wpilibc/src/main/native/cpp/RobotController.cpp index 8b2b4964a8d..2414ed7fbe3 100644 --- a/wpilibc/src/main/native/cpp/RobotController.cpp +++ b/wpilibc/src/main/native/cpp/RobotController.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "frc/Errors.h" @@ -230,6 +231,30 @@ units::celsius_t RobotController::GetCPUTemp() { return units::celsius_t{retVal}; } +static_assert(RadioLEDState::kOff == + static_cast(HAL_RadioLEDState::HAL_RadioLED_kOff)); +static_assert( + RadioLEDState::kGreen == + static_cast(HAL_RadioLEDState::HAL_RadioLED_kGreen)); +static_assert(RadioLEDState::kRed == + static_cast(HAL_RadioLEDState::HAL_RadioLED_kRed)); +static_assert( + RadioLEDState::kOrange == + static_cast(HAL_RadioLEDState::HAL_RadioLED_kOrange)); + +void RobotController::SetRadioLEDState(RadioLEDState state) { + int32_t status = 0; + HAL_SetRadioLEDState(static_cast(state), &status); + FRC_CheckErrorStatus(status, "SetRadioLEDState"); +} + +RadioLEDState RobotController::GetRadioLEDState() { + int32_t status = 0; + auto retVal = static_cast(HAL_GetRadioLEDState(&status)); + FRC_CheckErrorStatus(status, "GetRadioLEDState"); + return retVal; +} + CANStatus RobotController::GetCANStatus() { int32_t status = 0; float percentBusUtilization = 0; diff --git a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp index 90d9651d39f..728d358bfb1 100644 --- a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp +++ b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp @@ -338,6 +338,23 @@ void RoboRioSim::SetComments(std::string_view comments) { HALSIM_SetRoboRioComments(comments.data(), comments.size()); } +std::unique_ptr RoboRioSim::RegisterRadioLEDStateCallback( + NotifyCallback callback, bool initialNotify) { + auto store = std::make_unique( + -1, callback, &HALSIM_CancelRoboRioRadioLEDStateCallback); + store->SetUid(HALSIM_RegisterRoboRioRadioLEDStateCallback( + &CallbackStoreThunk, store.get(), initialNotify)); + return store; +} + +RadioLEDState RoboRioSim::GetRadioLEDState() { + return static_cast(HALSIM_GetRoboRioRadioLEDState()); +} + +void RoboRioSim::SetRadioLEDState(RadioLEDState state) { + HALSIM_SetRoboRioRadioLEDState(static_cast(state)); +} + void RoboRioSim::ResetData() { HALSIM_ResetRoboRioData(); } diff --git a/wpilibc/src/main/native/include/frc/RobotController.h b/wpilibc/src/main/native/include/frc/RobotController.h index bf65a8cfaac..0e7a4665017 100644 --- a/wpilibc/src/main/native/include/frc/RobotController.h +++ b/wpilibc/src/main/native/include/frc/RobotController.h @@ -21,6 +21,8 @@ struct CANStatus { int transmitErrorCount; }; +enum RadioLEDState { kOff = 0, kGreen = 1, kRed = 2, kOrange = 3 }; + class RobotController { public: RobotController() = delete; @@ -274,6 +276,23 @@ class RobotController { */ static units::celsius_t GetCPUTemp(); + /** + * Set the state of the "Radio" LED. On the RoboRIO, this writes to sysfs, so + * this function should not be called multiple times per loop cycle to avoid + * overruns. + * @param state The state to set the LED to. + */ + static void SetRadioLEDState(RadioLEDState state); + + /** + * Get the state of the "Radio" LED. On the RoboRIO, this reads from sysfs, so + * this function should not be called multiple times per loop cycle to avoid + * overruns. + * + * @return The state of the LED. + */ + static RadioLEDState GetRadioLEDState(); + /** * Get the current status of the CAN bus. * diff --git a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h index 64e81e18397..64713473a9a 100644 --- a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h @@ -11,6 +11,7 @@ #include #include +#include "frc/RobotController.h" #include "frc/simulation/CallbackStore.h" namespace frc::sim { @@ -514,6 +515,32 @@ class RoboRioSim { */ static void SetComments(std::string_view comments); + /** + * Register a callback to be run whenever the Radio led state changes. + * + * @param callback the callback + * @param initialNotify whether the callback should be called with the + * initial value + * @return the CallbackStore object associated with this callback + */ + [[nodiscard]] + static std::unique_ptr RegisterRadioLEDStateCallback( + NotifyCallback callback, bool initialNotify); + + /** + * Get the state of the radio led. + * + * @return The state of the radio led. + */ + static RadioLEDState GetRadioLEDState(); + + /** + * Set the state of the radio led. + * + * @param state The state of the radio led. + */ + static void SetRadioLEDState(RadioLEDState state); + /** * Reset all simulation data. */ diff --git a/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp b/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp index e42f38fd137..3b74a9157a8 100644 --- a/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp +++ b/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp @@ -274,4 +274,26 @@ TEST(RoboRioSimTest, SetComments) { EXPECT_EQ(kCommentsTruncated, RobotController::GetComments()); } +TEST(RoboRioSimTest, SetRadioLEDState) { + RoboRioSim::ResetData(); + + EnumCallback callback; + auto cbHandle = + RoboRioSim::RegisterRadioLEDStateCallback(callback.GetCallback(), false); + + RobotController::SetRadioLEDState(RadioLEDState::kGreen); + EXPECT_TRUE(callback.WasTriggered()); + EXPECT_EQ(RadioLEDState::kGreen, callback.GetLastValue()); + EXPECT_EQ(RadioLEDState::kGreen, RoboRioSim::GetRadioLEDState()); + EXPECT_EQ(RadioLEDState::kGreen, RobotController::GetRadioLEDState()); + + callback.Reset(); + + RoboRioSim::SetRadioLEDState(RadioLEDState::kOrange); + EXPECT_TRUE(callback.WasTriggered()); + EXPECT_EQ(RadioLEDState::kOrange, callback.GetLastValue()); + EXPECT_EQ(RadioLEDState::kOrange, RoboRioSim::GetRadioLEDState()); + EXPECT_EQ(RadioLEDState::kOrange, RobotController::GetRadioLEDState()); +} + } // namespace frc::sim diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java index 80d493ed517..3e25ce6dc99 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java @@ -6,6 +6,7 @@ import edu.wpi.first.hal.HAL; import edu.wpi.first.hal.HALUtil; +import edu.wpi.first.hal.LEDJNI; import edu.wpi.first.hal.PowerJNI; import edu.wpi.first.hal.can.CANJNI; import edu.wpi.first.hal.can.CANStatus; @@ -315,6 +316,66 @@ public static double getCPUTemp() { return PowerJNI.getCPUTemp(); } + /** State for the radio led. */ + public enum RadioLEDState { + /** Off. */ + kOff(LEDJNI.RADIO_LED_STATE_OFF), + /** Green. */ + kGreen(LEDJNI.RADIO_LED_STATE_GREEN), + /** Red. */ + kRed(LEDJNI.RADIO_LED_STATE_RED), + /** Orange. */ + kOrange(LEDJNI.RADIO_LED_STATE_ORANGE); + + /** The native value for this state. */ + public final int value; + + RadioLEDState(int value) { + this.value = value; + } + + /** + * Gets a state from an int value. + * + * @param value int value + * @return state + */ + public static RadioLEDState fromValue(int value) { + switch (value) { + case LEDJNI.RADIO_LED_STATE_OFF: + return RadioLEDState.kOff; + case LEDJNI.RADIO_LED_STATE_GREEN: + return RadioLEDState.kGreen; + case LEDJNI.RADIO_LED_STATE_RED: + return RadioLEDState.kRed; + case LEDJNI.RADIO_LED_STATE_ORANGE: + return RadioLEDState.kOrange; + default: + return RadioLEDState.kOff; + } + } + } + + /** + * Set the state of the "Radio" LED. On the RoboRIO, this writes to sysfs, so this function should + * not be called multiple times per loop cycle to avoid overruns. + * + * @param state The state to set the LED to. + */ + public static void setRadioLEDState(RadioLEDState state) { + LEDJNI.setRadioLEDState(state.value); + } + + /** + * Get the state of the "Radio" LED. On the RoboRIO, this reads from sysfs, so this function + * should not be called multiple times per loop cycle to avoid overruns. + * + * @return The state of the LED. + */ + public static RadioLEDState getRadioLEDState() { + return RadioLEDState.fromValue(LEDJNI.getRadioLEDState()); + } + /** * Get the current status of the CAN bus. * diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java index 8b28b6ddf53..e2bc895127b 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/simulation/RoboRioSim.java @@ -6,6 +6,8 @@ import edu.wpi.first.hal.simulation.NotifyCallback; import edu.wpi.first.hal.simulation.RoboRioDataJNI; +import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.RobotController.RadioLEDState; /** A utility class to control a simulated RoboRIO. */ public final class RoboRioSim { @@ -625,6 +627,38 @@ public static void setComments(String comments) { RoboRioDataJNI.setComments(comments); } + /** + * Register a callback to be run whenever the Radio led state changes. + * + * @param callback the callback + * @param initialNotify whether to call the callback with the initial state + * @return the {@link CallbackStore} object associated with this callback. Save a reference to + * this object so GC doesn't cancel the callback. + */ + public static CallbackStore registerRadioLEDStateCallback( + NotifyCallback callback, boolean initialNotify) { + int uid = RoboRioDataJNI.registerRadioLEDStateCallback(callback, initialNotify); + return new CallbackStore(uid, RoboRioDataJNI::cancelRadioLEDStateCallback); + } + + /** + * Get the state of the radio led. + * + * @return The state of the radio led. + */ + public static RadioLEDState getRadioLEDState() { + return RadioLEDState.fromValue(RoboRioDataJNI.getRadioLEDState()); + } + + /** + * Set the state of the radio led. + * + * @param state The state of the radio led. + */ + public static void setRadioLEDState(RobotController.RadioLEDState state) { + RoboRioDataJNI.setRadioLEDState(state.value); + } + /** Reset all simulation data. */ public static void resetData() { RoboRioDataJNI.resetData(); diff --git a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java index c016ec420c4..01029cd793e 100644 --- a/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java +++ b/wpilibj/src/test/java/edu/wpi/first/wpilibj/simulation/RoboRioSimTest.java @@ -10,8 +10,10 @@ import edu.wpi.first.hal.HALUtil; import edu.wpi.first.wpilibj.RobotController; +import edu.wpi.first.wpilibj.RobotController.RadioLEDState; import edu.wpi.first.wpilibj.simulation.testutils.BooleanCallback; import edu.wpi.first.wpilibj.simulation.testutils.DoubleCallback; +import edu.wpi.first.wpilibj.simulation.testutils.EnumCallback; import edu.wpi.first.wpilibj.simulation.testutils.IntCallback; import org.junit.jupiter.api.Test; @@ -279,4 +281,26 @@ void testComments() { assertEquals(kCommentsTruncated, RoboRioSim.getComments()); assertEquals(kCommentsTruncated, RobotController.getComments()); } + + @Test + void testRadioLEDState() { + RoboRioSim.resetData(); + + EnumCallback callback = new EnumCallback(); + try (CallbackStore cb = RoboRioSim.registerRadioLEDStateCallback(callback, false)) { + RobotController.setRadioLEDState(RadioLEDState.kGreen); + assertTrue(callback.wasTriggered()); + assertEquals(RadioLEDState.kGreen.value, callback.getSetValue()); + assertEquals(RadioLEDState.kGreen, RoboRioSim.getRadioLEDState()); + assertEquals(RadioLEDState.kGreen, RobotController.getRadioLEDState()); + + callback.reset(); + + RoboRioSim.setRadioLEDState(RadioLEDState.kOrange); + assertTrue(callback.wasTriggered()); + assertEquals(RadioLEDState.kOrange.value, callback.getSetValue()); + assertEquals(RadioLEDState.kOrange, RoboRioSim.getRadioLEDState()); + assertEquals(RadioLEDState.kOrange, RobotController.getRadioLEDState()); + } + } }