From 1c00a52b67769f24e1c012f5200a8e9f1841f634 Mon Sep 17 00:00:00 2001 From: Ryan Blue Date: Wed, 7 Jun 2023 12:54:03 -0400 Subject: [PATCH] [hal] Expose CAN timestamp base clock (#5357) --- .../java/edu/wpi/first/hal/CANAPIJNI.java | 2 ++ .../main/java/edu/wpi/first/hal/CANData.java | 7 +++++-- hal/src/main/native/athena/CANAPI.cpp | 20 +++++++++--------- hal/src/main/native/cpp/jni/CANAPIJNI.cpp | 12 +++++++++++ hal/src/main/native/include/hal/CANAPI.h | 21 +++++++++++++------ hal/src/main/native/sim/CANAPI.cpp | 20 +++++++++--------- wpilibc/src/main/native/cpp/CAN.cpp | 4 ++++ wpilibc/src/main/native/include/frc/CAN.h | 12 +++++++++++ .../main/java/edu/wpi/first/wpilibj/CAN.java | 11 ++++++++++ 9 files changed, 81 insertions(+), 28 deletions(-) diff --git a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java index 227da4d816..d95cf4541d 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/CANAPIJNI.java @@ -5,6 +5,8 @@ package edu.wpi.first.hal; public class CANAPIJNI extends JNIWrapper { + public static native long getCANPacketBaseTime(); + public static native int initializeCAN(int manufacturer, int deviceId, int deviceType); public static native void cleanCAN(int handle); diff --git a/hal/src/main/java/edu/wpi/first/hal/CANData.java b/hal/src/main/java/edu/wpi/first/hal/CANData.java index 0a644f6494..9136222626 100644 --- a/hal/src/main/java/edu/wpi/first/hal/CANData.java +++ b/hal/src/main/java/edu/wpi/first/hal/CANData.java @@ -6,16 +6,19 @@ package edu.wpi.first.hal; @SuppressWarnings("MemberName") public class CANData { + /** Contents of the CAN frame. */ public final byte[] data = new byte[8]; + /** Length of the frame in bytes. */ public int length; + /** CAN frame timestamp in milliseconds. */ public long timestamp; /** * API used from JNI to set the data. * * @param length Length of packet in bytes. - * @param timestamp CAN frame timestamp in microseconds. - * @return Buffer containing CAN frame. + * @param timestamp CAN frame timestamp in milliseconds. + * @return Buffer to place CAN frame data in. */ @SuppressWarnings("PMD.MethodReturnsInternalArray") public byte[] setData(int length, long timestamp) { diff --git a/hal/src/main/native/athena/CANAPI.cpp b/hal/src/main/native/athena/CANAPI.cpp index a3e4904cca..70f09e06f3 100644 --- a/hal/src/main/native/athena/CANAPI.cpp +++ b/hal/src/main/native/athena/CANAPI.cpp @@ -35,15 +35,6 @@ struct CANStorage { static UnlimitedHandleResource* canHandles; -static uint32_t GetPacketBaseTime() { - timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - - // Convert t to milliseconds - uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull; - return ms & 0xFFFFFFFF; -} - namespace hal::init { void InitializeCANAPI() { static UnlimitedHandleResource @@ -63,6 +54,15 @@ static int32_t CreateCANId(CANStorage* storage, int32_t apiId) { extern "C" { +uint32_t HAL_GetCANPacketBaseTime(void) { + timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + + // Convert t to milliseconds + uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull; + return ms & 0xFFFFFFFF; +} + HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer, int32_t deviceId, HAL_CANDeviceType deviceType, int32_t* status) { @@ -267,7 +267,7 @@ void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId, auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough - uint32_t now = GetPacketBaseTime(); + uint32_t now = HAL_GetCANPacketBaseTime(); if (now - i->second.lastTimeStamp > static_cast(timeoutMs)) { // Timeout, return bad status *status = HAL_CAN_TIMEOUT; diff --git a/hal/src/main/native/cpp/jni/CANAPIJNI.cpp b/hal/src/main/native/cpp/jni/CANAPIJNI.cpp index 106167abf0..e1299a8f7a 100644 --- a/hal/src/main/native/cpp/jni/CANAPIJNI.cpp +++ b/hal/src/main/native/cpp/jni/CANAPIJNI.cpp @@ -18,6 +18,18 @@ using namespace hal; using namespace wpi::java; extern "C" { + +/* + * Class: edu_wpi_first_hal_CANAPIJNI + * Method: getCANPacketBaseTime + * Signature: ()J + */ +JNIEXPORT jlong JNICALL +Java_edu_wpi_first_hal_CANAPIJNI_getCANPacketBaseTime + (JNIEnv*, jclass) +{ + return HAL_GetCANPacketBaseTime(); +} /* * Class: edu_wpi_first_hal_CANAPIJNI * Method: initializeCAN diff --git a/hal/src/main/native/include/hal/CANAPI.h b/hal/src/main/native/include/hal/CANAPI.h index d5244b5efd..005aa1bc19 100644 --- a/hal/src/main/native/include/hal/CANAPI.h +++ b/hal/src/main/native/include/hal/CANAPI.h @@ -19,6 +19,15 @@ extern "C" { #endif +/** + * Reads the current value of the millisecond-resolution timer that the CAN API + * functions use as a time base. + * + * @return Current value of timer used as a base time by the CAN API in + * milliseconds. + */ +uint32_t HAL_GetCANPacketBaseTime(void); + /** * Initializes a CAN device. * @@ -111,8 +120,8 @@ void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId, * @param[in] apiId the ID to read (0-1023) * @param[out] data the packet data (8 bytes) * @param[out] length the received length (0-8 bytes) - * @param[out] receivedTimestamp the packet received timestamp (based off of - * CLOCK_MONOTONIC) + * @param[out] receivedTimestamp the packet received timestamp in ms (based off + * of CLOCK_MONOTONIC) * @param[out] status Error status variable. 0 on success. */ void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data, @@ -127,8 +136,8 @@ void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data, * @param[in] apiId the ID to read (0-1023) * @param[out] data the packet data (8 bytes) * @param[out] length the received length (0-8 bytes) - * @param[out] receivedTimestamp the packet received timestamp (based off of - * CLOCK_MONOTONIC) + * @param[out] receivedTimestamp the packet received timestamp in ms (based off + * of CLOCK_MONOTONIC) * @param[out] status Error status variable. 0 on success. */ void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data, @@ -144,8 +153,8 @@ void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data, * @param[in] apiId the ID to read (0-1023) * @param[out] data the packet data (8 bytes) * @param[out] length the received length (0-8 bytes) - * @param[out] receivedTimestamp the packet received timestamp (based off of - * CLOCK_MONOTONIC) + * @param[out] receivedTimestamp the packet received timestamp in ms (based off + * of CLOCK_MONOTONIC) * @param[out] timeoutMs the timeout time for the packet * @param[out] status Error status variable. 0 on success. */ diff --git a/hal/src/main/native/sim/CANAPI.cpp b/hal/src/main/native/sim/CANAPI.cpp index 38014bd945..0eae18aaa7 100644 --- a/hal/src/main/native/sim/CANAPI.cpp +++ b/hal/src/main/native/sim/CANAPI.cpp @@ -35,13 +35,6 @@ struct CANStorage { static UnlimitedHandleResource* canHandles; -static uint32_t GetPacketBaseTime() { - int status = 0; - auto basetime = HAL_GetFPGATime(&status); - // us to ms - return (basetime / 1000ull) & 0xFFFFFFFF; -} - namespace hal { namespace init { void InitializeCANAPI() { @@ -71,6 +64,13 @@ static int32_t CreateCANId(CANStorage* storage, int32_t apiId) { return createdId; } +uint32_t HAL_GetCANPacketBaseTime() { + int status = 0; + auto basetime = HAL_GetFPGATime(&status); + // us to ms + return (basetime / 1000ull) & 0xFFFFFFFF; +} + HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer, int32_t deviceId, HAL_CANDeviceType deviceType, int32_t* status) { @@ -275,7 +275,7 @@ void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId, auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough - uint32_t now = GetPacketBaseTime(); + uint32_t now = HAL_GetCANPacketBaseTime(); if (now - i->second.lastTimeStamp > static_cast(timeoutMs)) { // Timeout, return bad status *status = HAL_CAN_TIMEOUT; @@ -307,7 +307,7 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId, auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough - uint32_t now = GetPacketBaseTime(); + uint32_t now = HAL_GetCANPacketBaseTime(); if (now - i->second.lastTimeStamp < static_cast(periodMs)) { *status = 0; // Read the data from the stored message into the output @@ -337,7 +337,7 @@ void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId, auto i = can->receives.find(messageId); if (i != can->receives.end()) { // Found, check if new enough - uint32_t now = GetPacketBaseTime(); + uint32_t now = HAL_GetCANPacketBaseTime(); if (now - i->second.lastTimeStamp > static_cast(timeoutMs)) { // Timeout, return bad status *status = HAL_CAN_TIMEOUT; diff --git a/wpilibc/src/main/native/cpp/CAN.cpp b/wpilibc/src/main/native/cpp/CAN.cpp index fc4a82181e..aa0370f861 100644 --- a/wpilibc/src/main/native/cpp/CAN.cpp +++ b/wpilibc/src/main/native/cpp/CAN.cpp @@ -131,3 +131,7 @@ bool CAN::ReadPacketTimeout(int apiId, int timeoutMs, CANData* data) { return true; } } + +uint64_t CAN::GetTimestampBaseTime() { + return HAL_GetCANPacketBaseTime(); +} diff --git a/wpilibc/src/main/native/include/frc/CAN.h b/wpilibc/src/main/native/include/frc/CAN.h index 4c9b9bd917..dc04988f1f 100644 --- a/wpilibc/src/main/native/include/frc/CAN.h +++ b/wpilibc/src/main/native/include/frc/CAN.h @@ -10,8 +10,11 @@ namespace frc { struct CANData { + /** Contents of the CAN packet. */ uint8_t data[8]; + /** Length of packet in bytes. */ int32_t length; + /** CAN frame timestamp in milliseconds. */ uint64_t timestamp; }; @@ -158,6 +161,15 @@ class CAN { */ bool ReadPacketTimeout(int apiId, int timeoutMs, CANData* data); + /** + * Reads the current value of the millisecond-resolution timer that CANData + * timestamps are based on + * + * @return Current value of timer used as a base time for CANData timestamps + * in milliseconds + */ + static uint64_t GetTimestampBaseTime(); + static constexpr HAL_CANManufacturer kTeamManufacturer = HAL_CAN_Man_kTeamUse; static constexpr HAL_CANDeviceType kTeamDeviceType = HAL_CAN_Dev_kMiscellaneous; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java index 77ccb3741b..8808a4b68b 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/CAN.java @@ -172,4 +172,15 @@ public class CAN implements Closeable { public boolean readPacketTimeout(int apiId, int timeoutMs, CANData data) { return CANAPIJNI.readCANPacketTimeout(m_handle, apiId, timeoutMs, data); } + + /** + * Reads the current value of the millisecond-resolution timer that {@link CANData} timestamps are + * based on. + * + * @return Current value of timer used as a base time for {@link CANData} timestamps in + * milliseconds + */ + public static long getTimestampBaseTime() { + return CANAPIJNI.getCANPacketBaseTime(); + } }