mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
470 lines
14 KiB
C++
470 lines
14 KiB
C++
/*----------------------------------------------------------------------------*/
|
|
/* Copyright (c) 2016-2020 FIRST. All Rights Reserved. */
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
/* the project. */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#include "HALUtil.h"
|
|
|
|
#include <jni.h>
|
|
|
|
#include <cassert>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
#include <wpi/SmallString.h>
|
|
#include <wpi/jni_util.h>
|
|
#include <wpi/raw_ostream.h>
|
|
|
|
#include "edu_wpi_first_hal_HALUtil.h"
|
|
#include "hal/CAN.h"
|
|
#include "hal/DriverStation.h"
|
|
#include "hal/Errors.h"
|
|
#include "hal/HAL.h"
|
|
|
|
using namespace wpi::java;
|
|
|
|
#define kRioStatusOffset -63000
|
|
#define kRioStatusSuccess 0
|
|
#define kRIOStatusBufferInvalidSize (kRioStatusOffset - 80)
|
|
#define kRIOStatusOperationTimedOut -52007
|
|
#define kRIOStatusFeatureNotSupported (kRioStatusOffset - 193)
|
|
#define kRIOStatusResourceNotInitialized -52010
|
|
|
|
static JavaVM* jvm = nullptr;
|
|
static JException illegalArgExCls;
|
|
static JException boundaryExCls;
|
|
static JException allocationExCls;
|
|
static JException halHandleExCls;
|
|
static JException canInvalidBufferExCls;
|
|
static JException canMessageNotFoundExCls;
|
|
static JException canMessageNotAllowedExCls;
|
|
static JException canNotInitializedExCls;
|
|
static JException uncleanStatusExCls;
|
|
static JClass pwmConfigDataResultCls;
|
|
static JClass canStatusCls;
|
|
static JClass matchInfoDataCls;
|
|
static JClass accumulatorResultCls;
|
|
static JClass canDataCls;
|
|
static JClass halValueCls;
|
|
|
|
static const JClassInit classes[] = {
|
|
{"edu/wpi/first/hal/PWMConfigDataResult", &pwmConfigDataResultCls},
|
|
{"edu/wpi/first/hal/can/CANStatus", &canStatusCls},
|
|
{"edu/wpi/first/hal/MatchInfoData", &matchInfoDataCls},
|
|
{"edu/wpi/first/hal/AccumulatorResult", &accumulatorResultCls},
|
|
{"edu/wpi/first/hal/CANData", &canDataCls},
|
|
{"edu/wpi/first/hal/HALValue", &halValueCls}};
|
|
|
|
static const JExceptionInit exceptions[] = {
|
|
{"java/lang/IllegalArgumentException", &illegalArgExCls},
|
|
{"edu/wpi/first/hal/util/BoundaryException", &boundaryExCls},
|
|
{"edu/wpi/first/hal/util/AllocationException", &allocationExCls},
|
|
{"edu/wpi/first/hal/util/HalHandleException", &halHandleExCls},
|
|
{"edu/wpi/first/hal/can/CANInvalidBufferException", &canInvalidBufferExCls},
|
|
{"edu/wpi/first/hal/can/CANMessageNotFoundException",
|
|
&canMessageNotFoundExCls},
|
|
{"edu/wpi/first/hal/can/CANMessageNotAllowedException",
|
|
&canMessageNotAllowedExCls},
|
|
{"edu/wpi/first/hal/can/CANNotInitializedException",
|
|
&canNotInitializedExCls},
|
|
{"edu/wpi/first/hal/util/UncleanStatusException", &uncleanStatusExCls}};
|
|
|
|
namespace hal {
|
|
|
|
void ThrowUncleanStatusException(JNIEnv* env, wpi::StringRef msg,
|
|
int32_t status) {
|
|
static jmethodID func =
|
|
env->GetMethodID(uncleanStatusExCls, "<init>", "(ILjava/lang/String;)V");
|
|
|
|
jobject exception =
|
|
env->NewObject(uncleanStatusExCls, func, static_cast<jint>(status),
|
|
MakeJString(env, msg));
|
|
env->Throw(static_cast<jthrowable>(exception));
|
|
}
|
|
|
|
void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange,
|
|
int32_t requestedValue, int32_t status) {
|
|
const char* message = HAL_GetErrorMessage(status);
|
|
wpi::SmallString<1024> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << " Code: " << status << ". " << message
|
|
<< ", Minimum Value: " << minRange << ", Maximum Value: " << maxRange
|
|
<< ", Requested Value: " << requestedValue;
|
|
env->ThrowNew(allocationExCls, buf.c_str());
|
|
allocationExCls.Throw(env, buf.c_str());
|
|
}
|
|
|
|
void ThrowHalHandleException(JNIEnv* env, int32_t status) {
|
|
const char* message = HAL_GetErrorMessage(status);
|
|
wpi::SmallString<1024> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << " Code: " << status << ". " << message;
|
|
halHandleExCls.Throw(env, buf.c_str());
|
|
}
|
|
|
|
void ReportError(JNIEnv* env, int32_t status, bool doThrow) {
|
|
if (status == 0) return;
|
|
if (status == HAL_HANDLE_ERROR) {
|
|
ThrowHalHandleException(env, status);
|
|
}
|
|
const char* message = HAL_GetErrorMessage(status);
|
|
if (doThrow && status < 0) {
|
|
wpi::SmallString<1024> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << " Code: " << status << ". " << message;
|
|
ThrowUncleanStatusException(env, buf.c_str(), status);
|
|
} else {
|
|
std::string func;
|
|
auto stack = GetJavaStackTrace(env, &func, "edu.wpi.first");
|
|
HAL_SendError(1, status, 0, message, func.c_str(), stack.c_str(), 1);
|
|
}
|
|
}
|
|
|
|
void ThrowError(JNIEnv* env, int32_t status, int32_t minRange, int32_t maxRange,
|
|
int32_t requestedValue) {
|
|
if (status == 0) return;
|
|
if (status == NO_AVAILABLE_RESOURCES || status == RESOURCE_IS_ALLOCATED ||
|
|
status == RESOURCE_OUT_OF_RANGE) {
|
|
ThrowAllocationException(env, minRange, maxRange, requestedValue, status);
|
|
}
|
|
if (status == HAL_HANDLE_ERROR) {
|
|
ThrowHalHandleException(env, status);
|
|
}
|
|
const char* message = HAL_GetErrorMessage(status);
|
|
wpi::SmallString<1024> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << " Code: " << status << ". " << message;
|
|
ThrowUncleanStatusException(env, buf.c_str(), status);
|
|
}
|
|
|
|
void ReportCANError(JNIEnv* env, int32_t status, int message_id) {
|
|
if (status >= 0) return;
|
|
switch (status) {
|
|
case kRioStatusSuccess:
|
|
// Everything is ok... don't throw.
|
|
break;
|
|
case HAL_ERR_CANSessionMux_InvalidBuffer:
|
|
case kRIOStatusBufferInvalidSize: {
|
|
static jmethodID invalidBufConstruct = nullptr;
|
|
if (!invalidBufConstruct)
|
|
invalidBufConstruct =
|
|
env->GetMethodID(canInvalidBufferExCls, "<init>", "()V");
|
|
jobject exception =
|
|
env->NewObject(canInvalidBufferExCls, invalidBufConstruct);
|
|
env->Throw(static_cast<jthrowable>(exception));
|
|
break;
|
|
}
|
|
case HAL_ERR_CANSessionMux_MessageNotFound:
|
|
case kRIOStatusOperationTimedOut: {
|
|
static jmethodID messageNotFoundConstruct = nullptr;
|
|
if (!messageNotFoundConstruct)
|
|
messageNotFoundConstruct =
|
|
env->GetMethodID(canMessageNotFoundExCls, "<init>", "()V");
|
|
jobject exception =
|
|
env->NewObject(canMessageNotFoundExCls, messageNotFoundConstruct);
|
|
env->Throw(static_cast<jthrowable>(exception));
|
|
break;
|
|
}
|
|
case HAL_ERR_CANSessionMux_NotAllowed:
|
|
case kRIOStatusFeatureNotSupported: {
|
|
wpi::SmallString<100> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << "MessageID = " << message_id;
|
|
canMessageNotAllowedExCls.Throw(env, buf.c_str());
|
|
break;
|
|
}
|
|
case HAL_ERR_CANSessionMux_NotInitialized:
|
|
case kRIOStatusResourceNotInitialized: {
|
|
static jmethodID notInitConstruct = nullptr;
|
|
if (!notInitConstruct)
|
|
notInitConstruct =
|
|
env->GetMethodID(canNotInitializedExCls, "<init>", "()V");
|
|
jobject exception =
|
|
env->NewObject(canNotInitializedExCls, notInitConstruct);
|
|
env->Throw(static_cast<jthrowable>(exception));
|
|
break;
|
|
}
|
|
default: {
|
|
wpi::SmallString<100> buf;
|
|
wpi::raw_svector_ostream oss(buf);
|
|
oss << "Fatal status code detected: " << status;
|
|
uncleanStatusExCls.Throw(env, buf.c_str());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ThrowIllegalArgumentException(JNIEnv* env, wpi::StringRef msg) {
|
|
illegalArgExCls.Throw(env, msg);
|
|
}
|
|
|
|
void ThrowBoundaryException(JNIEnv* env, double value, double lower,
|
|
double upper) {
|
|
static jmethodID getMessage = nullptr;
|
|
if (!getMessage)
|
|
getMessage = env->GetStaticMethodID(boundaryExCls, "getMessage",
|
|
"(DDD)Ljava/lang/String;");
|
|
|
|
static jmethodID constructor = nullptr;
|
|
if (!constructor)
|
|
constructor =
|
|
env->GetMethodID(boundaryExCls, "<init>", "(Ljava/lang/String;)V");
|
|
|
|
jobject msg = env->CallStaticObjectMethod(
|
|
boundaryExCls, getMessage, static_cast<jdouble>(value),
|
|
static_cast<jdouble>(lower), static_cast<jdouble>(upper));
|
|
jobject ex = env->NewObject(boundaryExCls, constructor, msg);
|
|
env->Throw(static_cast<jthrowable>(ex));
|
|
}
|
|
|
|
jobject CreatePWMConfigDataResult(JNIEnv* env, int32_t maxPwm,
|
|
int32_t deadbandMaxPwm, int32_t centerPwm,
|
|
int32_t deadbandMinPwm, int32_t minPwm) {
|
|
static jmethodID constructor =
|
|
env->GetMethodID(pwmConfigDataResultCls, "<init>", "(IIIII)V");
|
|
return env->NewObject(pwmConfigDataResultCls, constructor, (jint)maxPwm,
|
|
(jint)deadbandMaxPwm, (jint)centerPwm,
|
|
(jint)deadbandMinPwm, (jint)minPwm);
|
|
}
|
|
|
|
void SetCanStatusObject(JNIEnv* env, jobject canStatus,
|
|
float percentBusUtilization, uint32_t busOffCount,
|
|
uint32_t txFullCount, uint32_t receiveErrorCount,
|
|
uint32_t transmitErrorCount) {
|
|
static jmethodID func =
|
|
env->GetMethodID(canStatusCls, "setStatus", "(DIIII)V");
|
|
env->CallVoidMethod(canStatus, func, (jdouble)percentBusUtilization,
|
|
(jint)busOffCount, (jint)txFullCount,
|
|
(jint)receiveErrorCount, (jint)transmitErrorCount);
|
|
}
|
|
|
|
void SetMatchInfoObject(JNIEnv* env, jobject matchStatus,
|
|
const HAL_MatchInfo& matchInfo) {
|
|
static jmethodID func =
|
|
env->GetMethodID(matchInfoDataCls, "setData",
|
|
"(Ljava/lang/String;Ljava/lang/String;III)V");
|
|
|
|
env->CallVoidMethod(
|
|
matchStatus, func, MakeJString(env, matchInfo.eventName),
|
|
MakeJString(env, wpi::StringRef{reinterpret_cast<const char*>(
|
|
matchInfo.gameSpecificMessage),
|
|
matchInfo.gameSpecificMessageSize}),
|
|
(jint)matchInfo.matchNumber, (jint)matchInfo.replayNumber,
|
|
(jint)matchInfo.matchType);
|
|
}
|
|
|
|
void SetAccumulatorResultObject(JNIEnv* env, jobject accumulatorResult,
|
|
int64_t value, int64_t count) {
|
|
static jmethodID func =
|
|
env->GetMethodID(accumulatorResultCls, "set", "(JJ)V");
|
|
|
|
env->CallVoidMethod(accumulatorResult, func, (jlong)value, (jlong)count);
|
|
}
|
|
|
|
jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length,
|
|
uint64_t timestamp) {
|
|
static jmethodID func = env->GetMethodID(canDataCls, "setData", "(IJ)[B");
|
|
|
|
jbyteArray retVal = static_cast<jbyteArray>(
|
|
env->CallObjectMethod(canData, func, (jint)length, (jlong)timestamp));
|
|
return retVal;
|
|
}
|
|
|
|
jobject CreateHALValue(JNIEnv* env, const HAL_Value& value) {
|
|
static jmethodID fromNative = env->GetStaticMethodID(
|
|
halValueCls, "fromNative", "(IJD)Ledu/wpi/first/hal/HALValue;");
|
|
jlong value1 = 0;
|
|
jdouble value2 = 0.0;
|
|
switch (value.type) {
|
|
case HAL_BOOLEAN:
|
|
value1 = value.data.v_boolean;
|
|
break;
|
|
case HAL_DOUBLE:
|
|
value2 = value.data.v_double;
|
|
break;
|
|
case HAL_ENUM:
|
|
value1 = value.data.v_enum;
|
|
break;
|
|
case HAL_INT:
|
|
value1 = value.data.v_int;
|
|
break;
|
|
case HAL_LONG:
|
|
value1 = value.data.v_long;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return env->CallStaticObjectMethod(halValueCls, fromNative, (jint)value.type,
|
|
value1, value2);
|
|
}
|
|
|
|
JavaVM* GetJVM() { return jvm; }
|
|
|
|
namespace sim {
|
|
jint SimOnLoad(JavaVM* vm, void* reserved);
|
|
void SimOnUnload(JavaVM* vm, void* reserved);
|
|
} // namespace sim
|
|
|
|
} // namespace hal
|
|
|
|
using namespace hal;
|
|
|
|
extern "C" {
|
|
|
|
//
|
|
// indicate JNI version support desired and load classes
|
|
//
|
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
jvm = vm;
|
|
|
|
JNIEnv* env;
|
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
|
return JNI_ERR;
|
|
|
|
for (auto& c : classes) {
|
|
*c.cls = JClass(env, c.name);
|
|
if (!*c.cls) return JNI_ERR;
|
|
}
|
|
|
|
for (auto& c : exceptions) {
|
|
*c.cls = JException(env, c.name);
|
|
if (!*c.cls) return JNI_ERR;
|
|
}
|
|
|
|
return sim::SimOnLoad(vm, reserved);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|
sim::SimOnUnload(vm, reserved);
|
|
|
|
JNIEnv* env;
|
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
|
return;
|
|
// Delete global references
|
|
|
|
for (auto& c : classes) {
|
|
c.cls->free(env);
|
|
}
|
|
for (auto& c : exceptions) {
|
|
c.cls->free(env);
|
|
}
|
|
jvm = nullptr;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getFPGAVersion
|
|
* Signature: ()S
|
|
*/
|
|
JNIEXPORT jshort JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getFPGAVersion
|
|
(JNIEnv* env, jclass)
|
|
{
|
|
int32_t status = 0;
|
|
jshort returnValue = HAL_GetFPGAVersion(&status);
|
|
CheckStatus(env, status);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getFPGARevision
|
|
* Signature: ()I
|
|
*/
|
|
JNIEXPORT jint JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getFPGARevision
|
|
(JNIEnv* env, jclass)
|
|
{
|
|
int32_t status = 0;
|
|
jint returnValue = HAL_GetFPGARevision(&status);
|
|
CheckStatus(env, status);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getFPGATime
|
|
* Signature: ()J
|
|
*/
|
|
JNIEXPORT jlong JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getFPGATime
|
|
(JNIEnv* env, jclass)
|
|
{
|
|
int32_t status = 0;
|
|
jlong returnValue = HAL_GetFPGATime(&status);
|
|
CheckStatus(env, status);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getHALRuntimeType
|
|
* Signature: ()I
|
|
*/
|
|
JNIEXPORT jint JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getHALRuntimeType
|
|
(JNIEnv* env, jclass)
|
|
{
|
|
jint returnValue = HAL_GetRuntimeType();
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getFPGAButton
|
|
* Signature: ()Z
|
|
*/
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getFPGAButton
|
|
(JNIEnv* env, jclass)
|
|
{
|
|
int32_t status = 0;
|
|
jboolean returnValue = HAL_GetFPGAButton(&status);
|
|
CheckStatus(env, status);
|
|
return returnValue;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getHALErrorMessage
|
|
* Signature: (I)Ljava/lang/String;
|
|
*/
|
|
JNIEXPORT jstring JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getHALErrorMessage
|
|
(JNIEnv* paramEnv, jclass, jint paramId)
|
|
{
|
|
const char* msg = HAL_GetErrorMessage(paramId);
|
|
return MakeJString(paramEnv, msg);
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getHALErrno
|
|
* Signature: ()I
|
|
*/
|
|
JNIEXPORT jint JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getHALErrno
|
|
(JNIEnv*, jclass)
|
|
{
|
|
return errno;
|
|
}
|
|
|
|
/*
|
|
* Class: edu_wpi_first_hal_HALUtil
|
|
* Method: getHALstrerror
|
|
* Signature: (I)Ljava/lang/String;
|
|
*/
|
|
JNIEXPORT jstring JNICALL
|
|
Java_edu_wpi_first_hal_HALUtil_getHALstrerror
|
|
(JNIEnv* env, jclass, jint errorCode)
|
|
{
|
|
const char* msg = std::strerror(errno);
|
|
return MakeJString(env, msg);
|
|
}
|
|
|
|
} // extern "C"
|