diff --git a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java index ac91a6cec3..7c0f41a2f2 100644 --- a/hal/src/main/java/edu/wpi/first/hal/HALUtil.java +++ b/hal/src/main/java/edu/wpi/first/hal/HALUtil.java @@ -24,6 +24,8 @@ public final class HALUtil extends JNIWrapper { public static native String getSerialNumber(); + public static native String getComments(); + public static native long getFPGATime(); public static native int getHALRuntimeType(); 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 c7294654b8..ef06067dbe 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 @@ -155,5 +155,9 @@ public class RoboRioDataJNI extends JNIWrapper { public static native void setSerialNumber(String serialNumber); + public static native String getComments(); + + public static native void setComments(String comments); + public static native void resetData(); } diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index fd85319235..3a8cefdcbf 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,10 @@ static std::unique_ptr global; static std::unique_ptr watchdog; static uint64_t dsStartTime; +static char roboRioCommentsString[64]; +static size_t roboRioCommentsStringSize; +static bool roboRioCommentsStringInitialized; + using namespace hal; namespace hal { @@ -287,6 +292,67 @@ size_t HAL_GetSerialNumber(char* buffer, size_t size) { } } +void InitializeRoboRioComments(void) { + if (!roboRioCommentsStringInitialized) { + std::error_code ec; + std::unique_ptr fileBuffer = + wpi::MemoryBuffer::GetFile("/etc/machine-info", ec); + + std::string_view fileContents; + if (fileBuffer && !ec) { + fileContents = + std::string_view(reinterpret_cast(fileBuffer->begin()), + fileBuffer->size()); + } else { + roboRioCommentsStringSize = 0; + roboRioCommentsStringInitialized = true; + return; + } + std::string_view searchString = "PRETTY_HOSTNAME=\""; + + size_t start = fileContents.find(searchString); + if (start == std::string_view::npos) { + roboRioCommentsStringSize = 0; + roboRioCommentsStringInitialized = true; + return; + } + start += searchString.size(); + size_t end = fileContents.find("\"", start); + if (end == std::string_view::npos) { + end = fileContents.size(); + } + std::string_view escapedComments = wpi::slice(fileContents, start, end); + wpi::SmallString<64> buf; + auto [unescapedComments, rem] = wpi::UnescapeCString(escapedComments, buf); + unescapedComments.copy(roboRioCommentsString, + sizeof(roboRioCommentsString)); + + if (unescapedComments.size() > sizeof(roboRioCommentsString)) { + roboRioCommentsStringSize = sizeof(roboRioCommentsString); + } else { + roboRioCommentsStringSize = unescapedComments.size(); + } + roboRioCommentsStringInitialized = true; + } +} + +size_t HAL_GetComments(char* buffer, size_t size) { + if (!roboRioCommentsStringInitialized) { + InitializeRoboRioComments(); + } + size_t toCopy = size; + if (size > roboRioCommentsStringSize) { + toCopy = roboRioCommentsStringSize; + } + std::memcpy(buffer, roboRioCommentsString, toCopy); + if (toCopy < size) { + buffer[toCopy] = '\0'; + } else { + buffer[toCopy - 1] = '\0'; + } + return toCopy; +} + uint64_t HAL_GetFPGATime(int32_t* status) { hal::init::CheckInit(); if (!global) { diff --git a/hal/src/main/native/athena/mockdata/RoboRioData.cpp b/hal/src/main/native/athena/mockdata/RoboRioData.cpp index 9436629e8d..9392fcb6f4 100644 --- a/hal/src/main/native/athena/mockdata/RoboRioData.cpp +++ b/hal/src/main/native/athena/mockdata/RoboRioData.cpp @@ -42,6 +42,19 @@ size_t HALSIM_GetRoboRioSerialNumber(char* buffer, size_t size) { } void HALSIM_SetRoboRioSerialNumber(const char* buffer, size_t size) {} +int32_t HALSIM_RegisterRoboRioCommentsCallback( + HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) { + return 0; +} +void HALSIM_CancelRoboRioCommentsCallback(int32_t uid) {} +size_t HALSIM_GetRoboRioComments(char* buffer, size_t size) { + if (size > 0) { + buffer[0] = '\0'; + } + return 0; +} +void HALSIM_SetRoboRioComments(const char* buffer, size_t size) {} + void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify) {} } // extern "C" diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp index 4f4ed3f97a..76c05f8a44 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.cpp +++ b/hal/src/main/native/cpp/jni/HALUtil.cpp @@ -470,6 +470,20 @@ Java_edu_wpi_first_hal_HALUtil_getSerialNumber return MakeJString(env, std::string_view(serialNum, len)); } +/* + * Class: edu_wpi_first_hal_HALUtil + * Method: getComments + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_edu_wpi_first_hal_HALUtil_getComments + (JNIEnv* env, jclass) +{ + char comments[65]; + size_t len = HAL_GetComments(comments, sizeof(comments)); + return MakeJString(env, std::string_view(comments, len)); +} + /* * Class: edu_wpi_first_hal_HALUtil * Method: getFPGATime diff --git a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp index 289f9b017c..5bcd11471b 100644 --- a/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp +++ b/hal/src/main/native/cpp/jni/simulation/RoboRioDataJNI.cpp @@ -856,6 +856,33 @@ Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setSerialNumber serialNumberJString.size()); } +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: getComments + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_getComments + (JNIEnv* env, jclass) +{ + char comments[65]; + size_t len = HALSIM_GetRoboRioComments(comments, sizeof(comments)); + return MakeJString(env, std::string_view(comments, len)); +} + +/* + * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI + * Method: setComments + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_simulation_RoboRioDataJNI_setComments + (JNIEnv* env, jclass, jstring comments) +{ + JStringRef commentsJString{env, comments}; + HALSIM_SetRoboRioComments(commentsJString.c_str(), commentsJString.size()); +} + /* * Class: edu_wpi_first_hal_simulation_RoboRioDataJNI * Method: resetData diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h index d2e97cdb21..b4188e8ca9 100644 --- a/hal/src/main/native/include/hal/HALBase.h +++ b/hal/src/main/native/include/hal/HALBase.h @@ -81,6 +81,13 @@ int64_t HAL_GetFPGARevision(int32_t* status); */ size_t HAL_GetSerialNumber(char* buffer, size_t size); +/** + * Returns the comments from the roboRIO web interface. + * + * @return Comments. + */ +size_t HAL_GetComments(char* buffer, size_t size); + /** * Returns the runtime type of the HAL. * diff --git a/hal/src/main/native/include/hal/simulation/RoboRioData.h b/hal/src/main/native/include/hal/simulation/RoboRioData.h index 9c12bd55ff..864be5cf10 100644 --- a/hal/src/main/native/include/hal/simulation/RoboRioData.h +++ b/hal/src/main/native/include/hal/simulation/RoboRioData.h @@ -132,6 +132,12 @@ void HALSIM_CancelRoboRioSerialNumberCallback(int32_t uid); size_t HALSIM_GetRoboRioSerialNumber(char* buffer, size_t size); void HALSIM_SetRoboRioSerialNumber(const char* serialNumber, size_t size); +int32_t HALSIM_RegisterRoboRioCommentsCallback( + HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify); +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); diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index e7d5a348ae..82dd8c4ec1 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -284,6 +284,10 @@ size_t HAL_GetSerialNumber(char* buffer, size_t size) { return HALSIM_GetRoboRioSerialNumber(buffer, size); } +size_t HAL_GetComments(char* buffer, size_t size) { + return HALSIM_GetRoboRioComments(buffer, size); +} + uint64_t HAL_GetFPGATime(int32_t* status) { return hal::GetFPGATime(); } diff --git a/hal/src/main/native/sim/mockdata/RoboRioData.cpp b/hal/src/main/native/sim/mockdata/RoboRioData.cpp index 32f8c3db9f..b73b0d90d7 100644 --- a/hal/src/main/native/sim/mockdata/RoboRioData.cpp +++ b/hal/src/main/native/sim/mockdata/RoboRioData.cpp @@ -33,6 +33,7 @@ void RoboRioData::ResetData() { userFaults3V3.Reset(0); brownoutVoltage.Reset(6.75); m_serialNumber = ""; + m_comments = ""; } int32_t RoboRioData::RegisterSerialNumberCallback( @@ -72,6 +73,40 @@ void RoboRioData::SetSerialNumber(const char* serialNumber, size_t size) { m_serialNumberCallbacks(m_serialNumber.c_str(), m_serialNumber.size()); } +int32_t RoboRioData::RegisterCommentsCallback( + HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) { + std::scoped_lock lock(m_commentsMutex); + int32_t uid = m_commentsCallbacks.Register(callback, param); + if (initialNotify) { + callback(GetCommentsName(), param, m_comments.c_str(), + m_serialNumber.size()); + } + return uid; +} + +void RoboRioData::CancelCommentsCallback(int32_t uid) { + m_commentsCallbacks.Cancel(uid); +} + +size_t RoboRioData::GetComments(char* buffer, size_t size) { + std::scoped_lock lock(m_commentsMutex); + size_t copied = m_comments.copy(buffer, size); + // Null terminate if there is room + if (copied < size) { + buffer[copied] = '\0'; + } + return copied; +} + +void RoboRioData::SetComments(const char* comments, size_t size) { + if (size > 64) { + size = 64; + } + std::scoped_lock lock(m_commentsMutex); + m_comments = std::string(comments, size); + m_commentsCallbacks(m_comments.c_str(), m_comments.size()); +} + extern "C" { void HALSIM_ResetRoboRioData(void) { SimRoboRioData->ResetData(); @@ -113,6 +148,21 @@ void HALSIM_SetRoboRioSerialNumber(const char* serialNumber, size_t size) { SimRoboRioData->SetSerialNumber(serialNumber, size); } +int32_t HALSIM_RegisterRoboRioCommentsCallback( + HAL_RoboRioStringCallback callback, void* param, HAL_Bool initialNotify) { + return SimRoboRioData->RegisterCommentsCallback(callback, param, + initialNotify); +} +void HALSIM_CancelRoboRioCommentsCallback(int32_t uid) { + SimRoboRioData->CancelCommentsCallback(uid); +} +size_t HALSIM_GetRoboRioComments(char* buffer, size_t size) { + return SimRoboRioData->GetComments(buffer, size); +} +void HALSIM_SetRoboRioComments(const char* comments, size_t size) { + SimRoboRioData->SetComments(comments, size); +} + void HALSIM_RegisterRoboRioAllCallbacks(HAL_NotifyCallback callback, void* param, HAL_Bool initialNotify); diff --git a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h index 7638b722f1..c3ff17a4a2 100644 --- a/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h +++ b/hal/src/main/native/sim/mockdata/RoboRioDataInternal.h @@ -32,6 +32,7 @@ class RoboRioData { HAL_SIMDATAVALUE_DEFINE_NAME(BrownoutVoltage) HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(SerialNumber) + HAL_SIMCALLBACKREGISTRY_DEFINE_NAME(Comments); public: SimDataValue fpgaButton{false}; @@ -63,14 +64,26 @@ class RoboRioData { size_t GetSerialNumber(char* buffer, size_t size); void SetSerialNumber(const char* serialNumber, size_t size); + int32_t RegisterCommentsCallback(HAL_RoboRioStringCallback callback, + void* param, HAL_Bool initialNotify); + void CancelCommentsCallback(int32_t uid); + size_t GetComments(char* buffer, size_t size); + void SetComments(const char* comments, size_t size); + virtual void ResetData(); private: wpi::spinlock m_serialNumberMutex; std::string m_serialNumber; + wpi::spinlock m_commentsMutex; + std::string m_comments; + SimCallbackRegistry m_serialNumberCallbacks; + + SimCallbackRegistry + m_commentsCallbacks; }; extern RoboRioData* SimRoboRioData; } // namespace hal diff --git a/wpilibc/src/main/native/cpp/RobotController.cpp b/wpilibc/src/main/native/cpp/RobotController.cpp index 5beccde07f..da6e364131 100644 --- a/wpilibc/src/main/native/cpp/RobotController.cpp +++ b/wpilibc/src/main/native/cpp/RobotController.cpp @@ -35,6 +35,12 @@ std::string RobotController::GetSerialNumber() { return std::string(serialNum, len); } +std::string RobotController::GetComments() { + char comments[65]; + size_t len = HAL_GetComments(comments, sizeof(comments)); + return std::string(comments, len); +} + uint64_t RobotController::GetFPGATime() { int32_t status = 0; uint64_t time = HAL_GetFPGATime(&status); diff --git a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp index 19efb0cb10..6d0f809282 100644 --- a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp +++ b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp @@ -294,6 +294,16 @@ void RoboRioSim::SetSerialNumber(std::string_view serialNumber) { HALSIM_SetRoboRioSerialNumber(serialNumber.data(), serialNumber.size()); } +std::string RoboRioSim::GetComments() { + char comments[65]; + size_t len = HALSIM_GetRoboRioComments(comments, sizeof(comments)); + return std::string(comments, len); +} + +void RoboRioSim::SetComments(std::string_view comments) { + HALSIM_SetRoboRioComments(comments.data(), comments.size()); +} + 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 2355139f1b..cbb42d001b 100644 --- a/wpilibc/src/main/native/include/frc/RobotController.h +++ b/wpilibc/src/main/native/include/frc/RobotController.h @@ -45,12 +45,19 @@ class RobotController { static int64_t GetFPGARevision(); /** - * Returns the serial number of the roboRIO. + * Return the serial number of the roboRIO. * * @return The serial number of the roboRIO. */ static std::string GetSerialNumber(); + /** + * Return the comments from the roboRIO web interface. + * + * @return The comments from the roboRIO web interface. + */ + static std::string GetComments(); + /** * Read the microsecond-resolution timer on the FPGA. * diff --git a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h index 5fbc72217b..ee959b66cc 100644 --- a/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h +++ b/wpilibc/src/main/native/include/frc/simulation/RoboRioSim.h @@ -433,6 +433,20 @@ class RoboRioSim { */ static void SetSerialNumber(std::string_view serialNumber); + /** + * Get the comments. + * + * @return The comments. + */ + static std::string GetComments(); + + /** + * Set the comments. + * + * @param comments The comments. + */ + static void SetComments(std::string_view comments); + /** * 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 406e56fa53..98d4620fdf 100644 --- a/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp +++ b/wpilibc/src/test/native/cpp/simulation/RoboRioSimTest.cpp @@ -224,4 +224,24 @@ TEST(RoboRioSimTest, SetSerialNumber) { EXPECT_EQ(kSerialNumberTruncated, RobotController::GetSerialNumber()); } +TEST(RoboRioSimTest, SetComments) { + const std::string kComments = + "Hello! These are comments in the roboRIO web interface!"; + + RoboRioSim::ResetData(); + + RoboRioSim::SetComments(kComments); + EXPECT_EQ(kComments, RoboRioSim::GetComments()); + EXPECT_EQ(kComments, RobotController::GetComments()); + + const std::string kCommentsOverflow = + "Hello! These are comments in the roboRIO web interface! This comment " + "exceeds 64 characters!"; + const std::string kCommentsTruncated = kCommentsOverflow.substr(0, 64); + + RoboRioSim::SetComments(kCommentsOverflow); + EXPECT_EQ(kCommentsTruncated, RoboRioSim::GetComments()); + EXPECT_EQ(kCommentsTruncated, RobotController::GetComments()); +} + } // 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 4f5da38885..1dfeb3bc22 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/RobotController.java @@ -45,6 +45,15 @@ public final class RobotController { return HALUtil.getSerialNumber(); } + /** + * Return the comments from the roboRIO web interface. + * + * @return the comments from the roboRIO web interface. + */ + public static String getComments() { + return HALUtil.getComments(); + } + /** * Read the microsecond timer from the FPGA. * 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 3888ed562f..d128da8148 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 @@ -543,6 +543,24 @@ public final class RoboRioSim { RoboRioDataJNI.setSerialNumber(serialNumber); } + /** + * Get the comments string. + * + * @return The comments string. + */ + public static String getComments() { + return RoboRioDataJNI.getComments(); + } + + /** + * Set the comments string. + * + * @param comments The comments string. + */ + public static void setComments(String comments) { + RoboRioDataJNI.setComments(comments); + } + /** 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 3bba249f98..9b15b7772a 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 @@ -226,4 +226,23 @@ class RoboRioSimTest { assertEquals(kSerialNumberTruncated, RoboRioSim.getSerialNumber()); assertEquals(kSerialNumberTruncated, RobotController.getSerialNumber()); } + + @Test + void testComments() { + RoboRioSim.resetData(); + + final String kComments = "Hello! These are comments in the roboRIO web interface!"; + + RoboRioSim.setComments(kComments); + assertEquals(kComments, RoboRioSim.getComments()); + assertEquals(kComments, RobotController.getComments()); + + final String kCommentsOverflow = + "Hello! These are comments in the roboRIO web interface!" + + " This comment exceeds 64 characters!"; + final String kCommentsTruncated = kCommentsOverflow.substring(0, 64); + RoboRioSim.setComments(kCommentsOverflow); + assertEquals(kCommentsTruncated, RoboRioSim.getComments()); + assertEquals(kCommentsTruncated, RobotController.getComments()); + } }