diff --git a/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java new file mode 100644 index 0000000000..bdb2112854 --- /dev/null +++ b/hal/src/main/java/edu/wpi/first/hal/CANStreamMessage.java @@ -0,0 +1,35 @@ +// 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 CANStreamMessage { + @SuppressWarnings("MemberName") + public final byte[] data = new byte[8]; + + @SuppressWarnings("MemberName") + public int length; + + @SuppressWarnings("MemberName") + public long timestamp; + + @SuppressWarnings("MemberName") + public int messageID; + + /** + * API used from JNI to set the data. + * + * @param length Length of packet in bytes. + * @param messageID CAN message ID of the message. + * @param timestamp CAN frame timestamp in microseconds. + * @return Buffer containing CAN frame. + */ + @SuppressWarnings("PMD.MethodReturnsInternalArray") + public byte[] setStreamData(int length, int messageID, long timestamp) { + this.messageID = messageID; + this.length = length; + this.timestamp = timestamp; + return data; + } +} diff --git a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java index 6a498a6faf..e0734dd369 100644 --- a/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java +++ b/hal/src/main/java/edu/wpi/first/hal/can/CANJNI.java @@ -4,6 +4,7 @@ package edu.wpi.first.hal.can; +import edu.wpi.first.hal.CANStreamMessage; import edu.wpi.first.hal.JNIWrapper; import java.nio.ByteBuffer; import java.nio.IntBuffer; @@ -24,4 +25,11 @@ public class CANJNI extends JNIWrapper { IntBuffer messageID, int messageIDMask, ByteBuffer timeStamp); public static native void getCANStatus(CANStatus status); + + public static native int openCANStreamSession(int messageID, int messageIDMask, int maxMessages); + + public static native void closeCANStreamSession(int sessionHandle); + + public static native int readCANStreamSession( + int sessionHandle, CANStreamMessage[] messages, int messagesToRead); } diff --git a/hal/src/main/native/cpp/jni/CANJNI.cpp b/hal/src/main/native/cpp/jni/CANJNI.cpp index d69786ed63..4ad2a74e04 100644 --- a/hal/src/main/native/cpp/jni/CANJNI.cpp +++ b/hal/src/main/native/cpp/jni/CANJNI.cpp @@ -91,4 +91,90 @@ Java_edu_wpi_first_hal_can_CANJNI_getCANStatus txFullCount, receiveErrorCount, transmitErrorCount); } +/* + * Class: edu_wpi_first_hal_can_CANJNI + * Method: openCANStreamSession + * Signature: (III)I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_can_CANJNI_openCANStreamSession + (JNIEnv* env, jclass, jint messageID, jint messageIDMask, jint maxMessages) +{ + uint32_t handle = 0; + int32_t status = 0; + HAL_CAN_OpenStreamSession(&handle, static_cast(messageID), + static_cast(messageIDMask), + static_cast(maxMessages), &status); + + if (!CheckStatus(env, status)) { + return static_cast(0); + } + + return static_cast(handle); +} + +/* + * Class: edu_wpi_first_hal_can_CANJNI + * Method: closeCANStreamSession + * Signature: (I)V + */ +JNIEXPORT void JNICALL +Java_edu_wpi_first_hal_can_CANJNI_closeCANStreamSession + (JNIEnv* env, jclass, jint sessionHandle) +{ + HAL_CAN_CloseStreamSession(static_cast(sessionHandle)); +} + +/* + * Class: edu_wpi_first_hal_can_CANJNI + * Method: readCANStreamSession + * Signature: (I[Ljava/lang/Object;I)I + */ +JNIEXPORT jint JNICALL +Java_edu_wpi_first_hal_can_CANJNI_readCANStreamSession + (JNIEnv* env, jclass, jint sessionHandle, jobjectArray messages, + jint messagesToRead) +{ + uint32_t handle = static_cast(sessionHandle); + uint32_t messagesRead = 0; + + wpi::SmallVector messageBuffer; + messageBuffer.resize_for_overwrite(messagesToRead); + + int32_t status = 0; + + HAL_CAN_ReadStreamSession(handle, messageBuffer.begin(), + static_cast(messagesToRead), + &messagesRead, &status); + + if (status == HAL_ERR_CANSessionMux_MessageNotFound || messagesRead == 0) { + return 0; + } + + if (!CheckStatus(env, status)) { + return 0; + } + + for (int i = 0; i < static_cast(messagesRead); i++) { + struct HAL_CANStreamMessage* msg = &messageBuffer[i]; + JLocal elem{ + env, static_cast(env->GetObjectArrayElement(messages, i))}; + if (!elem) { + // TODO decide if should throw + continue; + } + JLocal toSetArray{ + env, SetCANStreamObject(env, elem, msg->dataSize, msg->messageID, + msg->timeStamp)}; + auto javaLen = env->GetArrayLength(toSetArray); + if (javaLen < msg->dataSize) { + msg->dataSize = javaLen; + } + env->SetByteArrayRegion(toSetArray, 0, msg->dataSize, + reinterpret_cast(msg->data)); + } + + return static_cast(messagesRead); +} + } // extern "C" diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp index 5d5a9588b4..a2f080b872 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.cpp +++ b/hal/src/main/native/cpp/jni/HALUtil.cpp @@ -52,6 +52,7 @@ static JClass canStatusCls; static JClass matchInfoDataCls; static JClass accumulatorResultCls; static JClass canDataCls; +static JClass canStreamMessageCls; static JClass halValueCls; static JClass baseStoreCls; static JClass revPHVersionCls; @@ -64,6 +65,7 @@ static const JClassInit classes[] = { {"edu/wpi/first/hal/MatchInfoData", &matchInfoDataCls}, {"edu/wpi/first/hal/AccumulatorResult", &accumulatorResultCls}, {"edu/wpi/first/hal/CANData", &canDataCls}, + {"edu/wpi/first/hal/CANStreamMessage", &canStreamMessageCls}, {"edu/wpi/first/hal/HALValue", &halValueCls}, {"edu/wpi/first/hal/DMAJNISample$BaseStore", &baseStoreCls}, {"edu/wpi/first/hal/REVPHVersion", &revPHVersionCls}}; @@ -303,6 +305,18 @@ jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length, return retVal; } +jbyteArray SetCANStreamObject(JNIEnv* env, jobject canStreamData, + int32_t length, uint32_t messageID, + uint64_t timestamp) { + static jmethodID func = + env->GetMethodID(canStreamMessageCls, "setStreamData", "(IIJ)[B"); + + jbyteArray retVal = static_cast(env->CallObjectMethod( + canStreamData, func, static_cast(length), + static_cast(messageID), static_cast(timestamp))); + return retVal; +} + jobject CreateHALValue(JNIEnv* env, const HAL_Value& value) { static jmethodID fromNative = env->GetStaticMethodID( halValueCls, "fromNative", "(IJD)Ledu/wpi/first/hal/HALValue;"); diff --git a/hal/src/main/native/cpp/jni/HALUtil.h b/hal/src/main/native/cpp/jni/HALUtil.h index cf3956c7f3..9c9487cd30 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.h +++ b/hal/src/main/native/cpp/jni/HALUtil.h @@ -78,6 +78,10 @@ void SetAccumulatorResultObject(JNIEnv* env, jobject accumulatorResult, jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length, uint64_t timestamp); +jbyteArray SetCANStreamObject(JNIEnv* env, jobject canStreamData, + int32_t length, uint32_t messageID, + uint64_t timestamp); + jobject CreateHALValue(JNIEnv* env, const HAL_Value& value); jobject CreateDMABaseStore(JNIEnv* env, jint valueType, jint index);