Files
allwpilib/hal/src/main/native/cpp/jni/HALUtil.cpp

486 lines
14 KiB
C++
Raw Normal View History

// 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 "HALUtil.h"
#include <jni.h>
2018-05-13 17:09:56 -07:00
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <string>
2018-05-13 17:09:56 -07:00
#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;
2014-01-06 09:27:51 -05:00
#define kRioStatusOffset -63000
#define kRioStatusSuccess 0
#define kRIOStatusBufferInvalidSize (kRioStatusOffset - 80)
#define kRIOStatusOperationTimedOut -52007
#define kRIOStatusFeatureNotSupported (kRioStatusOffset - 193)
#define kRIOStatusResourceNotInitialized -52010
2018-05-13 17:09:56 -07:00
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;
2018-05-21 16:09:38 -07:00
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));
}
2018-05-13 17:09:56 -07:00
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);
2018-05-13 17:09:56 -07:00
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());
}
2018-05-13 17:09:56 -07:00
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());
}
2018-05-13 17:09:56 -07:00
void ReportError(JNIEnv* env, int32_t status, bool doThrow) {
if (status == 0) {
return;
}
if (status == HAL_HANDLE_ERROR) {
2016-07-09 01:12:37 -07:00
ThrowHalHandleException(env, status);
}
2018-05-13 17:09:56 -07:00
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);
}
}
2018-05-13 17:09:56 -07:00
void ThrowError(JNIEnv* env, int32_t status, int32_t minRange, int32_t maxRange,
int32_t requestedValue) {
if (status == 0) {
return;
}
2018-05-13 17:09:56 -07:00
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);
}
2018-05-13 17:09:56 -07:00
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);
}
2018-05-13 17:09:56 -07:00
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);
}
2018-05-13 17:09:56 -07:00
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");
}
2018-05-13 17:09:56 -07:00
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));
}
2018-05-13 17:09:56 -07:00
jobject CreatePWMConfigDataResult(JNIEnv* env, int32_t maxPwm,
int32_t deadbandMaxPwm, int32_t centerPwm,
int32_t deadbandMinPwm, int32_t minPwm) {
static jmethodID constructor =
2018-05-13 17:09:56 -07:00
env->GetMethodID(pwmConfigDataResultCls, "<init>", "(IIIII)V");
return env->NewObject(pwmConfigDataResultCls, constructor, (jint)maxPwm,
(jint)deadbandMaxPwm, (jint)centerPwm,
(jint)deadbandMinPwm, (jint)minPwm);
}
2018-05-13 17:09:56 -07:00
void SetCanStatusObject(JNIEnv* env, jobject canStatus,
float percentBusUtilization, uint32_t busOffCount,
uint32_t txFullCount, uint32_t receiveErrorCount,
uint32_t transmitErrorCount) {
2018-05-13 17:09:56 -07:00
static jmethodID func =
env->GetMethodID(canStatusCls, "setStatus", "(DIIII)V");
env->CallVoidMethod(canStatus, func, (jdouble)percentBusUtilization,
2018-05-13 17:09:56 -07:00
(jint)busOffCount, (jint)txFullCount,
(jint)receiveErrorCount, (jint)transmitErrorCount);
}
void SetMatchInfoObject(JNIEnv* env, jobject matchStatus,
const HAL_MatchInfo& matchInfo) {
2018-05-13 17:09:56 -07:00
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) {
2018-05-13 17:09:56 -07:00
static jmethodID func =
env->GetMethodID(accumulatorResultCls, "set", "(JJ)V");
env->CallVoidMethod(accumulatorResult, func, (jlong)value, (jlong)count);
}
2018-05-21 16:09:38 -07:00
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;
}
2018-04-29 13:29:07 -07:00
namespace sim {
2018-05-13 17:09:56 -07:00
jint SimOnLoad(JavaVM* vm, void* reserved);
void SimOnUnload(JavaVM* vm, void* reserved);
} // namespace sim
2018-04-29 13:29:07 -07:00
} // namespace hal
using namespace hal;
extern "C" {
2014-01-06 09:27:51 -05:00
//
// indicate JNI version support desired and load classes
2014-01-06 09:27:51 -05:00
//
2018-05-13 17:09:56 -07:00
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
jvm = vm;
2018-05-13 17:09:56 -07:00
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;
}
}
2018-05-21 16:09:38 -07:00
2018-04-29 13:29:07 -07:00
return sim::SimOnLoad(vm, reserved);
2014-01-06 09:27:51 -05:00
}
2018-05-13 17:09:56 -07:00
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
2018-04-29 13:29:07 -07:00
sim::SimOnUnload(vm, reserved);
2018-05-13 17:09:56 -07:00
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;
}
2014-01-06 09:27:51 -05:00
/*
* Class: edu_wpi_first_hal_HALUtil
2014-01-06 09:27:51 -05:00
* Method: getFPGAVersion
* Signature: ()S
2014-01-06 09:27:51 -05:00
*/
JNIEXPORT jshort JNICALL
Java_edu_wpi_first_hal_HALUtil_getFPGAVersion
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass)
{
int32_t status = 0;
jshort returnValue = HAL_GetFPGAVersion(&status);
CheckStatus(env, status);
return returnValue;
2014-01-06 09:27:51 -05:00
}
/*
* Class: edu_wpi_first_hal_HALUtil
2014-01-06 09:27:51 -05:00
* Method: getFPGARevision
* Signature: ()I
2014-01-06 09:27:51 -05:00
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_HALUtil_getFPGARevision
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass)
{
int32_t status = 0;
jint returnValue = HAL_GetFPGARevision(&status);
CheckStatus(env, status);
return returnValue;
2014-01-06 09:27:51 -05:00
}
/*
* Class: edu_wpi_first_hal_HALUtil
2014-01-06 09:27:51 -05:00
* Method: getFPGATime
* Signature: ()J
2014-01-06 09:27:51 -05:00
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_hal_HALUtil_getFPGATime
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass)
{
int32_t status = 0;
jlong returnValue = HAL_GetFPGATime(&status);
CheckStatus(env, status);
return returnValue;
2014-01-06 09:27:51 -05:00
}
/*
* Class: edu_wpi_first_hal_HALUtil
* Method: getHALRuntimeType
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_HALUtil_getHALRuntimeType
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass)
{
jint returnValue = HAL_GetRuntimeType();
return returnValue;
}
/*
* Class: edu_wpi_first_hal_HALUtil
* Method: getFPGAButton
2018-05-13 17:09:56 -07:00
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_edu_wpi_first_hal_HALUtil_getFPGAButton
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass)
{
int32_t status = 0;
jboolean returnValue = HAL_GetFPGAButton(&status);
CheckStatus(env, status);
return returnValue;
}
2014-01-06 09:27:51 -05:00
/*
* Class: edu_wpi_first_hal_HALUtil
* Method: getHALErrorMessage
2014-01-06 09:27:51 -05:00
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_edu_wpi_first_hal_HALUtil_getHALErrorMessage
2018-05-13 17:09:56 -07:00
(JNIEnv* paramEnv, jclass, jint paramId)
{
const char* msg = HAL_GetErrorMessage(paramId);
return MakeJString(paramEnv, msg);
2014-01-06 09:27:51 -05:00
}
/*
* Class: edu_wpi_first_hal_HALUtil
* Method: getHALErrno
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_HALUtil_getHALErrno
2018-05-13 17:09:56 -07:00
(JNIEnv*, jclass)
{
return errno;
}
/*
* Class: edu_wpi_first_hal_HALUtil
* Method: getHALstrerror
* Signature: (I)Ljava/lang/String;
*/
2018-05-13 17:09:56 -07:00
JNIEXPORT jstring JNICALL
Java_edu_wpi_first_hal_HALUtil_getHALstrerror
2018-05-13 17:09:56 -07:00
(JNIEnv* env, jclass, jint errorCode)
{
const char* msg = std::strerror(errno);
return MakeJString(env, msg);
}
} // extern "C"