| #include <jni.h> |
| #include <atomic> |
| #include <cassert> |
| #include <condition_variable> |
| #include <mutex> |
| #include <sstream> |
| #include <queue> |
| #include <thread> |
| |
| #include "edu_wpi_first_wpilibj_networktables_NetworkTablesJNI.h" |
| #include "ntcore.h" |
| #include "atomic_static.h" |
| |
| // |
| // Globals and load/unload |
| // |
| |
| // Used for callback. |
| static JavaVM *jvm = nullptr; |
| static jclass booleanCls = nullptr; |
| static jclass doubleCls = nullptr; |
| static jclass stringCls = nullptr; |
| static jclass connectionInfoCls = nullptr; |
| static jclass entryInfoCls = nullptr; |
| static jclass keyNotDefinedEx = nullptr; |
| static jclass persistentEx = nullptr; |
| // Thread-attached environment for listener callbacks. |
| static JNIEnv *listenerEnv = nullptr; |
| |
| static void ListenerOnStart() { |
| if (!jvm) return; |
| JNIEnv *env; |
| if (jvm->AttachCurrentThread(reinterpret_cast<void **>(&env), |
| nullptr) != JNI_OK) |
| return; |
| if (!env || !env->functions) return; |
| listenerEnv = env; |
| } |
| |
| static void ListenerOnExit() { |
| listenerEnv = nullptr; |
| if (!jvm) return; |
| jvm->DetachCurrentThread(); |
| } |
| |
| extern "C" { |
| |
| 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; |
| |
| // Cache references to classes |
| jclass local; |
| |
| local = env->FindClass("java/lang/Boolean"); |
| if (!local) return JNI_ERR; |
| booleanCls = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!booleanCls) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = env->FindClass("java/lang/Double"); |
| if (!local) return JNI_ERR; |
| doubleCls = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!doubleCls) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = env->FindClass("java/lang/String"); |
| if (!local) return JNI_ERR; |
| stringCls = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!stringCls) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = env->FindClass("edu/wpi/first/wpilibj/networktables/ConnectionInfo"); |
| if (!local) return JNI_ERR; |
| connectionInfoCls = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!connectionInfoCls) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = env->FindClass("edu/wpi/first/wpilibj/networktables/EntryInfo"); |
| if (!local) return JNI_ERR; |
| entryInfoCls = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!entryInfoCls) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = |
| env->FindClass("edu/wpi/first/wpilibj/networktables/NetworkTableKeyNotDefined"); |
| keyNotDefinedEx = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!keyNotDefinedEx) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| local = |
| env->FindClass("edu/wpi/first/wpilibj/networktables/PersistentException"); |
| persistentEx = static_cast<jclass>(env->NewGlobalRef(local)); |
| if (!persistentEx) return JNI_ERR; |
| env->DeleteLocalRef(local); |
| |
| // Initial configuration of listener start/exit |
| nt::SetListenerOnStart(ListenerOnStart); |
| nt::SetListenerOnExit(ListenerOnExit); |
| |
| return JNI_VERSION_1_6; |
| } |
| |
| JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { |
| JNIEnv *env; |
| if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) |
| return; |
| // Delete global references |
| if (booleanCls) env->DeleteGlobalRef(booleanCls); |
| if (doubleCls) env->DeleteGlobalRef(doubleCls); |
| if (stringCls) env->DeleteGlobalRef(stringCls); |
| if (connectionInfoCls) env->DeleteGlobalRef(connectionInfoCls); |
| if (entryInfoCls) env->DeleteGlobalRef(entryInfoCls); |
| if (keyNotDefinedEx) env->DeleteGlobalRef(keyNotDefinedEx); |
| if (persistentEx) env->DeleteGlobalRef(persistentEx); |
| jvm = nullptr; |
| } |
| |
| } // extern "C" |
| |
| // |
| // Helper class to automatically clean up a local reference |
| // |
| template <typename T> |
| class JavaLocal { |
| public: |
| JavaLocal(JNIEnv *env, T obj) : m_env(env), m_obj(obj) {} |
| ~JavaLocal() { |
| if (m_obj) m_env->DeleteLocalRef(m_obj); |
| } |
| operator T() { return m_obj; } |
| T obj() { return m_obj; } |
| |
| private: |
| JNIEnv *m_env; |
| T m_obj; |
| }; |
| |
| // |
| // Helper class to create and clean up a global reference |
| // |
| template <typename T> |
| class JavaGlobal { |
| public: |
| JavaGlobal(JNIEnv *env, T obj) |
| : m_obj(static_cast<T>(env->NewGlobalRef(obj))) {} |
| ~JavaGlobal() { |
| if (!jvm || nt::NotifierDestroyed()) return; |
| JNIEnv *env; |
| if (jvm->AttachCurrentThread(reinterpret_cast<void **>(&env), nullptr) != |
| JNI_OK) |
| return; |
| if (!env || !env->functions) return; |
| env->DeleteGlobalRef(m_obj); |
| jvm->DetachCurrentThread(); |
| } |
| operator T() { return m_obj; } |
| T obj() { return m_obj; } |
| |
| private: |
| T m_obj; |
| }; |
| |
| // |
| // Helper class to create and clean up a weak global reference |
| // |
| template <typename T> |
| class JavaWeakGlobal { |
| public: |
| JavaWeakGlobal(JNIEnv *env, T obj) |
| : m_obj(static_cast<T>(env->NewWeakGlobalRef(obj))) {} |
| ~JavaWeakGlobal() { |
| if (!jvm || nt::NotifierDestroyed()) return; |
| JNIEnv *env; |
| if (jvm->AttachCurrentThread(reinterpret_cast<void **>(&env), nullptr) != |
| JNI_OK) |
| return; |
| if (!env || !env->functions) return; |
| env->DeleteWeakGlobalRef(m_obj); |
| jvm->DetachCurrentThread(); |
| } |
| JavaLocal<T> obj(JNIEnv *env) { |
| return JavaLocal<T>(env, env->NewLocalRef(m_obj)); |
| } |
| |
| private: |
| T m_obj; |
| }; |
| |
| // |
| // Conversions from Java objects to C++ |
| // |
| |
| class JavaStringRef { |
| public: |
| JavaStringRef(JNIEnv *env, jstring str) |
| : m_env(env), |
| m_jstr(str), |
| m_str(env->GetStringUTFChars(str, nullptr)) {} |
| ~JavaStringRef() { m_env->ReleaseStringUTFChars(m_jstr, m_str); } |
| |
| operator nt::StringRef() const { return nt::StringRef(m_str); } |
| nt::StringRef str() const { return nt::StringRef(m_str); } |
| const char *c_str() const { return m_str; } |
| |
| private: |
| JNIEnv *m_env; |
| jstring m_jstr; |
| const char *m_str; |
| }; |
| |
| class JavaByteRef { |
| public: |
| JavaByteRef(JNIEnv *env, jbyteArray jarr) |
| : m_env(env), |
| m_jarr(jarr), |
| m_elements(env->GetByteArrayElements(jarr, nullptr)), |
| m_size(env->GetArrayLength(jarr)) {} |
| ~JavaByteRef() { |
| m_env->ReleaseByteArrayElements(m_jarr, m_elements, JNI_ABORT); |
| } |
| |
| operator nt::StringRef() const { |
| return nt::StringRef(reinterpret_cast<char *>(m_elements), m_size); |
| } |
| |
| private: |
| JNIEnv *m_env; |
| jbyteArray m_jarr; |
| jbyte *m_elements; |
| size_t m_size; |
| }; |
| |
| class JavaByteRefBB { |
| public: |
| JavaByteRefBB(JNIEnv *env, jobject bb, int len) |
| : m_elements(env->GetDirectBufferAddress(bb)), m_size(len) {} |
| |
| operator nt::StringRef() const { |
| return nt::StringRef(reinterpret_cast<char *>(m_elements), m_size); |
| } |
| |
| private: |
| void *m_elements; |
| size_t m_size; |
| }; |
| |
| std::shared_ptr<nt::Value> FromJavaRaw(JNIEnv *env, jbyteArray jarr) { |
| size_t len = env->GetArrayLength(jarr); |
| jbyte *elements = |
| static_cast<jbyte *>(env->GetPrimitiveArrayCritical(jarr, nullptr)); |
| if (!elements) return nullptr; |
| auto rv = nt::Value::MakeRaw( |
| nt::StringRef(reinterpret_cast<char *>(elements), len)); |
| env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); |
| return rv; |
| } |
| |
| std::shared_ptr<nt::Value> FromJavaRawBB(JNIEnv *env, jobject jbb, int len) { |
| void* elements = env->GetDirectBufferAddress(jbb); |
| if (!elements) return nullptr; |
| auto rv = nt::Value::MakeRaw( |
| nt::StringRef(reinterpret_cast<char *>(elements), len)); |
| return rv; |
| } |
| |
| std::shared_ptr<nt::Value> FromJavaRpc(JNIEnv *env, jbyteArray jarr) { |
| size_t len = env->GetArrayLength(jarr); |
| jbyte *elements = |
| static_cast<jbyte *>(env->GetPrimitiveArrayCritical(jarr, nullptr)); |
| if (!elements) return nullptr; |
| auto rv = nt::Value::MakeRpc( |
| nt::StringRef(reinterpret_cast<char *>(elements), len)); |
| env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); |
| return rv; |
| } |
| |
| std::shared_ptr<nt::Value> FromJavaBooleanArray(JNIEnv *env, |
| jbooleanArray jarr) { |
| size_t len = env->GetArrayLength(jarr); |
| std::vector<int> arr; |
| arr.reserve(len); |
| jboolean *elements = |
| static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); |
| if (!elements) return nullptr; |
| for (size_t i = 0; i < len; ++i) arr.push_back(elements[i]); |
| env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); |
| return nt::Value::MakeBooleanArray(arr); |
| } |
| |
| std::shared_ptr<nt::Value> FromJavaDoubleArray(JNIEnv *env, jdoubleArray jarr) { |
| size_t len = env->GetArrayLength(jarr); |
| jdouble *elements = |
| static_cast<jdouble *>(env->GetPrimitiveArrayCritical(jarr, nullptr)); |
| if (!elements) return nullptr; |
| auto rv = nt::Value::MakeDoubleArray(nt::ArrayRef<double>(elements, len)); |
| env->ReleasePrimitiveArrayCritical(jarr, elements, JNI_ABORT); |
| return rv; |
| } |
| |
| std::shared_ptr<nt::Value> FromJavaStringArray(JNIEnv *env, jobjectArray jarr) { |
| size_t len = env->GetArrayLength(jarr); |
| std::vector<std::string> arr; |
| arr.reserve(len); |
| for (size_t i = 0; i < len; ++i) { |
| JavaLocal<jstring> elem( |
| env, static_cast<jstring>(env->GetObjectArrayElement(jarr, i))); |
| if (!elem) return nullptr; |
| arr.push_back(JavaStringRef(env, elem).str()); |
| } |
| return nt::Value::MakeStringArray(std::move(arr)); |
| } |
| |
| // |
| // Conversions from C++ to Java objects |
| // |
| |
| static inline jstring ToJavaString(JNIEnv *env, nt::StringRef str) { |
| // fastpath if string already null terminated; if not, need to make a copy |
| if (str.data()[str.size()] == '\0') |
| return env->NewStringUTF(str.data()); |
| else |
| return env->NewStringUTF(str.str().c_str()); |
| } |
| |
| static jbyteArray ToJavaByteArray(JNIEnv *env, nt::StringRef str) { |
| jbyteArray jarr = env->NewByteArray(str.size()); |
| if (!jarr) return nullptr; |
| env->SetByteArrayRegion(jarr, 0, str.size(), |
| reinterpret_cast<const jbyte *>(str.data())); |
| return jarr; |
| } |
| |
| static jbooleanArray ToJavaBooleanArray(JNIEnv *env, nt::ArrayRef<int> arr) |
| { |
| jbooleanArray jarr = env->NewBooleanArray(arr.size()); |
| if (!jarr) return nullptr; |
| jboolean *elements = |
| static_cast<jboolean*>(env->GetPrimitiveArrayCritical(jarr, nullptr)); |
| if (!elements) return nullptr; |
| for (size_t i = 0; i < arr.size(); ++i) |
| elements[i] = arr[i]; |
| env->ReleasePrimitiveArrayCritical(jarr, elements, 0); |
| return jarr; |
| } |
| |
| static jdoubleArray ToJavaDoubleArray(JNIEnv *env, nt::ArrayRef<double> arr) |
| { |
| jdoubleArray jarr = env->NewDoubleArray(arr.size()); |
| if (!jarr) return nullptr; |
| env->SetDoubleArrayRegion(jarr, 0, arr.size(), arr.data()); |
| return jarr; |
| } |
| |
| static jobjectArray ToJavaStringArray(JNIEnv *env, |
| nt::ArrayRef<std::string> arr) { |
| jobjectArray jarr = env->NewObjectArray(arr.size(), stringCls, nullptr); |
| if (!jarr) return nullptr; |
| for (size_t i = 0; i < arr.size(); ++i) { |
| JavaLocal<jstring> elem(env, env->NewStringUTF(arr[i].c_str())); |
| env->SetObjectArrayElement(jarr, i, elem.obj()); |
| } |
| return jarr; |
| } |
| |
| static jobject ToJavaObject(JNIEnv *env, const nt::Value& value) { |
| static jmethodID booleanConstructor = nullptr; |
| static jmethodID doubleConstructor = nullptr; |
| if (!booleanConstructor) |
| booleanConstructor = env->GetMethodID(booleanCls, "<init>", "(Z)V"); |
| if (!doubleConstructor) |
| doubleConstructor = env->GetMethodID(doubleCls, "<init>", "(D)V"); |
| |
| switch (value.type()) { |
| case NT_BOOLEAN: |
| return env->NewObject(booleanCls, booleanConstructor, |
| (jboolean)(value.GetBoolean() ? 1 : 0)); |
| case NT_DOUBLE: |
| return env->NewObject(doubleCls, doubleConstructor, |
| (jdouble)value.GetDouble()); |
| case NT_STRING: |
| return ToJavaString(env, value.GetString()); |
| case NT_RAW: |
| return ToJavaByteArray(env, value.GetRaw()); |
| case NT_BOOLEAN_ARRAY: |
| return ToJavaBooleanArray(env, value.GetBooleanArray()); |
| case NT_DOUBLE_ARRAY: |
| return ToJavaDoubleArray(env, value.GetDoubleArray()); |
| case NT_STRING_ARRAY: |
| return ToJavaStringArray(env, value.GetStringArray()); |
| case NT_RPC: |
| return ToJavaByteArray(env, value.GetRpc()); |
| default: |
| return nullptr; |
| } |
| } |
| |
| static jobject ToJavaObject(JNIEnv *env, const nt::ConnectionInfo &info) { |
| static jmethodID constructor = |
| env->GetMethodID(connectionInfoCls, "<init>", |
| "(Ljava/lang/String;Ljava/lang/String;IJI)V"); |
| JavaLocal<jstring> remote_id(env, ToJavaString(env, info.remote_id)); |
| JavaLocal<jstring> remote_name(env, ToJavaString(env, info.remote_name)); |
| return env->NewObject(connectionInfoCls, constructor, remote_id.obj(), |
| remote_name.obj(), (jint)info.remote_port, |
| (jlong)info.last_update, (jint)info.protocol_version); |
| } |
| |
| static jobject ToJavaObject(JNIEnv *env, const nt::EntryInfo &info) { |
| static jmethodID constructor = |
| env->GetMethodID(entryInfoCls, "<init>", "(Ljava/lang/String;IIJ)V"); |
| JavaLocal<jstring> name(env, ToJavaString(env, info.name)); |
| return env->NewObject(entryInfoCls, constructor, name.obj(), (jint)info.type, |
| (jint)info.flags, (jlong)info.last_change); |
| } |
| |
| // |
| // Exception throwers |
| // |
| |
| static void ThrowTableKeyNotDefined(JNIEnv *env, jstring key) { |
| static jmethodID constructor = nullptr; |
| if (!constructor) |
| constructor = |
| env->GetMethodID(keyNotDefinedEx, "<init>", "(Ljava/lang/String;)V"); |
| jobject exception = env->NewObject(keyNotDefinedEx, constructor, key); |
| env->Throw(static_cast<jthrowable>(exception)); |
| } |
| |
| extern "C" { |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: containsKey |
| * Signature: (Ljava/lang/String;)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_containsKey |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val) return false; |
| return true; |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getType |
| * Signature: (Ljava/lang/String;)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getType |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val) return NT_UNASSIGNED; |
| return val->type(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putBoolean |
| * Signature: (Ljava/lang/String;Z)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBoolean |
| (JNIEnv *env, jclass, jstring key, jboolean value) |
| { |
| return nt::SetEntryValue(JavaStringRef(env, key), |
| nt::Value::MakeBoolean(value != JNI_FALSE)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putDouble |
| * Signature: (Ljava/lang/String;D)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDouble |
| (JNIEnv *env, jclass, jstring key, jdouble value) |
| { |
| return nt::SetEntryValue(JavaStringRef(env, key), |
| nt::Value::MakeDouble(value)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putString |
| * Signature: (Ljava/lang/String;Ljava/lang/String;)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putString |
| (JNIEnv *env, jclass, jstring key, jstring value) |
| { |
| return nt::SetEntryValue(JavaStringRef(env, key), |
| nt::Value::MakeString(JavaStringRef(env, value))); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putRaw |
| * Signature: (Ljava/lang/String;[B)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putRaw__Ljava_lang_String_2_3B |
| (JNIEnv *env, jclass, jstring key, jbyteArray value) |
| { |
| auto v = FromJavaRaw(env, value); |
| if (!v) return false; |
| return nt::SetEntryValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putRaw |
| * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putRaw__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I |
| (JNIEnv *env, jclass, jstring key, jobject value, jint len) |
| { |
| auto v = FromJavaRawBB(env, value, len); |
| if (!v) return false; |
| return nt::SetEntryValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putBooleanArray |
| * Signature: (Ljava/lang/String;[Z)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putBooleanArray |
| (JNIEnv *env, jclass, jstring key, jbooleanArray value) |
| { |
| auto v = FromJavaBooleanArray(env, value); |
| if (!v) return false; |
| return nt::SetEntryValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putDoubleArray |
| * Signature: (Ljava/lang/String;[D)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putDoubleArray |
| (JNIEnv *env, jclass, jstring key, jdoubleArray value) |
| { |
| auto v = FromJavaDoubleArray(env, value); |
| if (!v) return false; |
| return nt::SetEntryValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: putStringArray |
| * Signature: (Ljava/lang/String;[Ljava/lang/String;)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_putStringArray |
| (JNIEnv *env, jclass, jstring key, jobjectArray value) |
| { |
| auto v = FromJavaStringArray(env, value); |
| if (!v) return false; |
| return nt::SetEntryValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutBoolean |
| * Signature: (Ljava/lang/String;Z)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBoolean |
| (JNIEnv *env, jclass, jstring key, jboolean value) |
| { |
| nt::SetEntryTypeValue(JavaStringRef(env, key), nt::Value::MakeBoolean(value != JNI_FALSE)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutDouble |
| * Signature: (Ljava/lang/String;D)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDouble |
| (JNIEnv *env, jclass, jstring key, jdouble value) |
| { |
| nt::SetEntryTypeValue(JavaStringRef(env, key), nt::Value::MakeDouble(value)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutString |
| * Signature: (Ljava/lang/String;Ljava/lang/String;)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutString |
| (JNIEnv *env, jclass, jstring key, jstring value) |
| { |
| nt::SetEntryTypeValue(JavaStringRef(env, key), |
| nt::Value::MakeString(JavaStringRef(env, value))); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutRaw |
| * Signature: (Ljava/lang/String;[B)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutRaw__Ljava_lang_String_2_3B |
| (JNIEnv *env, jclass, jstring key, jbyteArray value) |
| { |
| auto v = FromJavaRaw(env, value); |
| if (!v) return; |
| nt::SetEntryTypeValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutRaw |
| * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutRaw__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I |
| (JNIEnv *env, jclass, jstring key, jobject value, jint len) |
| { |
| auto v = FromJavaRawBB(env, value, len); |
| if (!v) return; |
| nt::SetEntryTypeValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutBooleanArray |
| * Signature: (Ljava/lang/String;[Z)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutBooleanArray |
| (JNIEnv *env, jclass, jstring key, jbooleanArray value) |
| { |
| auto v = FromJavaBooleanArray(env, value); |
| if (!v) return; |
| nt::SetEntryTypeValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutDoubleArray |
| * Signature: (Ljava/lang/String;[D)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutDoubleArray |
| (JNIEnv *env, jclass, jstring key, jdoubleArray value) |
| { |
| auto v = FromJavaDoubleArray(env, value); |
| if (!v) return; |
| nt::SetEntryTypeValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: forcePutStringArray |
| * Signature: (Ljava/lang/String;[Ljava/lang/String;)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_forcePutStringArray |
| (JNIEnv *env, jclass, jstring key, jobjectArray value) |
| { |
| auto v = FromJavaStringArray(env, value); |
| if (!v) return; |
| nt::SetEntryTypeValue(JavaStringRef(env, key), v); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getValue |
| * Signature: (Ljava/lang/String;)Ljava/lang/Object; |
| */ |
| JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaObject(env, *val); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getBoolean |
| * Signature: (Ljava/lang/String;)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsBoolean()) { |
| ThrowTableKeyNotDefined(env, key); |
| return false; |
| } |
| return val->GetBoolean(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getDouble |
| * Signature: (Ljava/lang/String;)D |
| */ |
| JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsDouble()) { |
| ThrowTableKeyNotDefined(env, key); |
| return 0; |
| } |
| return val->GetDouble(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getString |
| * Signature: (Ljava/lang/String;)Ljava/lang/String; |
| */ |
| JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsString()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaString(env, val->GetString()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getRaw |
| * Signature: (Ljava/lang/String;)[B |
| */ |
| JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsRaw()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaByteArray(env, val->GetRaw()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getBooleanArray |
| * Signature: (Ljava/lang/String;)[Z |
| */ |
| JNIEXPORT jbooleanArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsBooleanArray()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaBooleanArray(env, val->GetBooleanArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getDoubleArray |
| * Signature: (Ljava/lang/String;)[D |
| */ |
| JNIEXPORT jdoubleArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsDoubleArray()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaDoubleArray(env, val->GetDoubleArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getStringArray |
| * Signature: (Ljava/lang/String;)[Ljava/lang/String; |
| */ |
| JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsStringArray()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaStringArray(env, val->GetStringArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getValue |
| * Signature: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; |
| */ |
| JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getValue__Ljava_lang_String_2Ljava_lang_Object_2 |
| (JNIEnv *env, jclass, jstring key, jobject defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val) return defaultValue; |
| return ToJavaObject(env, *val); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getBoolean |
| * Signature: (Ljava/lang/String;Z)Z |
| */ |
| JNIEXPORT jboolean JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBoolean__Ljava_lang_String_2Z |
| (JNIEnv *env, jclass, jstring key, jboolean defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsBoolean()) return defaultValue; |
| return val->GetBoolean(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getDouble |
| * Signature: (Ljava/lang/String;D)D |
| */ |
| JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDouble__Ljava_lang_String_2D |
| (JNIEnv *env, jclass, jstring key, jdouble defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsDouble()) return defaultValue; |
| return val->GetDouble(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getString |
| * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; |
| */ |
| JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getString__Ljava_lang_String_2Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key, jstring defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsString()) return defaultValue; |
| return ToJavaString(env, val->GetString()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getRaw |
| * Signature: (Ljava/lang/String;[B)[B |
| */ |
| JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRaw__Ljava_lang_String_2_3B |
| (JNIEnv *env, jclass, jstring key, jbyteArray defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsRaw()) return defaultValue; |
| return ToJavaByteArray(env, val->GetRaw()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getBooleanArray |
| * Signature: (Ljava/lang/String;[Z)[Z |
| */ |
| JNIEXPORT jbooleanArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getBooleanArray__Ljava_lang_String_2_3Z |
| (JNIEnv *env, jclass, jstring key, jbooleanArray defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsBooleanArray()) return defaultValue; |
| return ToJavaBooleanArray(env, val->GetBooleanArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getDoubleArray |
| * Signature: (Ljava/lang/String;[D)[D |
| */ |
| JNIEXPORT jdoubleArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getDoubleArray__Ljava_lang_String_2_3D |
| (JNIEnv *env, jclass, jstring key, jdoubleArray defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsDoubleArray()) return defaultValue; |
| return ToJavaDoubleArray(env, val->GetDoubleArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getStringArray |
| * Signature: (Ljava/lang/String;[Ljava/lang/String;)[Ljava/lang/String; |
| */ |
| JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getStringArray__Ljava_lang_String_2_3Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key, jobjectArray defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsStringArray()) return defaultValue; |
| return ToJavaStringArray(env, val->GetStringArray()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: setEntryFlags |
| * Signature: (Ljava/lang/String;I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setEntryFlags |
| (JNIEnv *env, jclass, jstring key, jint flags) |
| { |
| nt::SetEntryFlags(JavaStringRef(env, key), flags); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getEntryFlags |
| * Signature: (Ljava/lang/String;)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getEntryFlags |
| (JNIEnv *env, jclass, jstring key) |
| { |
| return nt::GetEntryFlags(JavaStringRef(env, key)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: deleteEntry |
| * Signature: (Ljava/lang/String;)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteEntry |
| (JNIEnv *env, jclass, jstring key) |
| { |
| nt::DeleteEntry(JavaStringRef(env, key)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: deleteAllEntries |
| * Signature: ()V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_deleteAllEntries |
| (JNIEnv *, jclass) |
| { |
| nt::DeleteAllEntries(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getEntries |
| * Signature: (Ljava/lang/String;I)[Ledu/wpi/first/wpilibj/networktables/EntryInfo; |
| */ |
| JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getEntries |
| (JNIEnv *env, jclass, jstring prefix, jint types) |
| { |
| auto arr = nt::GetEntryInfo(JavaStringRef(env, prefix), types); |
| jobjectArray jarr = env->NewObjectArray(arr.size(), entryInfoCls, nullptr); |
| if (!jarr) return nullptr; |
| for (size_t i = 0; i < arr.size(); ++i) { |
| JavaLocal<jobject> jelem(env, ToJavaObject(env, arr[i])); |
| env->SetObjectArrayElement(jarr, i, jelem); |
| } |
| return jarr; |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: flush |
| * Signature: ()V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_flush |
| (JNIEnv *, jclass) |
| { |
| nt::Flush(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: addEntryListener |
| * Signature: (Ljava/lang/String;Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/EntryListenerFunction;Z)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addEntryListener |
| (JNIEnv *envouter, jclass, jstring prefix, jobject listener, jint flags) |
| { |
| // the shared pointer to the weak global will keep it around until the |
| // entry listener is destroyed |
| auto listener_global = |
| std::make_shared<JavaGlobal<jobject>>(envouter, listener); |
| |
| // cls is a temporary here; cannot be used within callback functor |
| jclass cls = envouter->GetObjectClass(listener); |
| if (!cls) return 0; |
| |
| // method ids, on the other hand, are safe to retain |
| jmethodID mid = envouter->GetMethodID( |
| cls, "apply", "(ILjava/lang/String;Ljava/lang/Object;I)V"); |
| if (!mid) return 0; |
| |
| return nt::AddEntryListener( |
| JavaStringRef(envouter, prefix), |
| [=](unsigned int uid, nt::StringRef name, |
| std::shared_ptr<nt::Value> value, unsigned int flags_) { |
| JNIEnv *env = listenerEnv; |
| if (!env || !env->functions) return; |
| |
| // get the handler |
| auto handler = listener_global->obj(); |
| |
| // convert the value into the appropriate Java type |
| jobject jobj = ToJavaObject(env, *value); |
| if (!jobj) return; |
| |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| return; |
| } |
| env->CallVoidMethod(handler, mid, (jint)uid, ToJavaString(env, name), |
| jobj, (jint)(flags_)); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| }, |
| flags); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: removeEntryListener |
| * Signature: (I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeEntryListener |
| (JNIEnv *, jclass, jint entryListenerUid) |
| { |
| nt::RemoveEntryListener(entryListenerUid); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: addConnectionListener |
| * Signature: (Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/ConnectionListenerFunction;Z)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_addConnectionListener |
| (JNIEnv *envouter, jclass, jobject listener, jboolean immediateNotify) |
| { |
| // the shared pointer to the weak global will keep it around until the |
| // entry listener is destroyed |
| auto listener_global = |
| std::make_shared<JavaGlobal<jobject>>(envouter, listener); |
| |
| // cls is a temporary here; cannot be used within callback functor |
| jclass cls = envouter->GetObjectClass(listener); |
| if (!cls) return 0; |
| |
| // method ids, on the other hand, are safe to retain |
| jmethodID mid = envouter->GetMethodID( |
| cls, "apply", "(IZLedu/wpi/first/wpilibj/networktables/ConnectionInfo;)V"); |
| if (!mid) return 0; |
| |
| return nt::AddConnectionListener( |
| [=](unsigned int uid, bool connected, const nt::ConnectionInfo& conn) { |
| JNIEnv *env = listenerEnv; |
| if (!env || !env->functions) return; |
| |
| // get the handler |
| auto handler = listener_global->obj(); |
| //if (!handler) goto done; // can happen due to weak reference |
| |
| // convert into the appropriate Java type |
| jobject jobj = ToJavaObject(env, conn); |
| if (!jobj) return; |
| |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| return; |
| } |
| env->CallVoidMethod(handler, mid, (jint)uid, |
| (jboolean)(connected ? 1 : 0), jobj); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| }, |
| immediateNotify != JNI_FALSE); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: removeConnectionListener |
| * Signature: (I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_removeConnectionListener |
| (JNIEnv *, jclass, jint connListenerUid) |
| { |
| nt::RemoveConnectionListener(connListenerUid); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getRpc |
| * Signature: (Ljava/lang/String;)[B |
| */ |
| JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2 |
| (JNIEnv *env, jclass, jstring key) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsRpc()) { |
| ThrowTableKeyNotDefined(env, key); |
| return nullptr; |
| } |
| return ToJavaByteArray(env, val->GetRpc()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getRpc |
| * Signature: (Ljava/lang/String;[B)[B |
| */ |
| JNIEXPORT jbyteArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getRpc__Ljava_lang_String_2_3B |
| (JNIEnv *env, jclass, jstring key, jbyteArray defaultValue) |
| { |
| auto val = nt::GetEntryValue(JavaStringRef(env, key)); |
| if (!val || !val->IsRpc()) return defaultValue; |
| return ToJavaByteArray(env, val->GetRpc()); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: callRpc |
| * Signature: (Ljava/lang/String;[B)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc__Ljava_lang_String_2_3B |
| (JNIEnv *env, jclass, jstring key, jbyteArray params) |
| { |
| return nt::CallRpc(JavaStringRef(env, key), JavaByteRef(env, params)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: callRpc |
| * Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;I)I |
| */ |
| JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_callRpc__Ljava_lang_String_2Ljava_nio_ByteBuffer_2I |
| (JNIEnv *env, jclass, jstring key, jobject params, jint params_len) |
| { |
| return nt::CallRpc(JavaStringRef(env, key), |
| JavaByteRefBB(env, params, params_len)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: setNetworkIdentity |
| * Signature: (Ljava/lang/String;)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setNetworkIdentity |
| (JNIEnv *env, jclass, jstring name) |
| { |
| nt::SetNetworkIdentity(JavaStringRef(env, name)); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: startServer |
| * Signature: (Ljava/lang/String;Ljava/lang/String;I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startServer |
| (JNIEnv *env, jclass, jstring persistFilename, jstring listenAddress, |
| jint port) |
| { |
| nt::StartServer(JavaStringRef(env, persistFilename), |
| JavaStringRef(env, listenAddress).c_str(), port); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: stopServer |
| * Signature: ()V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopServer |
| (JNIEnv *, jclass) |
| { |
| nt::StopServer(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: startClient |
| * Signature: (Ljava/lang/String;I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_startClient |
| (JNIEnv *env, jclass, jstring serverName, jint port) |
| { |
| nt::StartClient(JavaStringRef(env, serverName).c_str(), port); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: stopClient |
| * Signature: ()V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_stopClient |
| (JNIEnv *, jclass) |
| { |
| nt::StopClient(); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: setUpdateRate |
| * Signature: (D)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setUpdateRate |
| (JNIEnv *, jclass, jdouble interval) |
| { |
| nt::SetUpdateRate(interval); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: getConnections |
| * Signature: ()[Ledu/wpi/first/wpilibj/networktables/ConnectionInfo; |
| */ |
| JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_getConnections |
| (JNIEnv *env, jclass) |
| { |
| auto arr = nt::GetConnections(); |
| jobjectArray jarr = |
| env->NewObjectArray(arr.size(), connectionInfoCls, nullptr); |
| if (!jarr) return nullptr; |
| for (size_t i = 0; i < arr.size(); ++i) { |
| JavaLocal<jobject> jelem(env, ToJavaObject(env, arr[i])); |
| env->SetObjectArrayElement(jarr, i, jelem); |
| } |
| return jarr; |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: savePersistent |
| * Signature: (Ljava/lang/String;)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_savePersistent |
| (JNIEnv *env, jclass, jstring filename) |
| { |
| const char *err = nt::SavePersistent(JavaStringRef(env, filename)); |
| if (err) env->ThrowNew(persistentEx, err); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: loadPersistent |
| * Signature: (Ljava/lang/String;)[Ljava/lang/String; |
| */ |
| JNIEXPORT jobjectArray JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_loadPersistent |
| (JNIEnv *env, jclass, jstring filename) |
| { |
| std::vector<std::string> warns; |
| const char *err = nt::LoadPersistent(JavaStringRef(env, filename), |
| [&](size_t line, const char *msg) { |
| std::ostringstream oss; |
| oss << line << ": " << msg; |
| warns.push_back(oss.str()); |
| }); |
| if (err) { |
| env->ThrowNew(persistentEx, err); |
| return nullptr; |
| } |
| return ToJavaStringArray(env, warns); |
| } |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: now |
| * Signature: ()J |
| */ |
| JNIEXPORT jlong JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_now |
| (JNIEnv *, jclass) |
| { |
| return nt::Now(); |
| } |
| |
| } // extern "C" |
| |
| // Thread where log callbacks are actually performed. |
| // |
| // JNI's AttachCurrentThread() creates a Java Thread object on every |
| // invocation, which is both time inefficient and causes issues with Eclipse |
| // (which tries to keep a thread list up-to-date and thus gets swamped). |
| // |
| // Instead, this class attaches just once. When a hardware notification |
| // occurs, a condition variable wakes up this thread and this thread actually |
| // makes the call into Java. |
| class LoggerThreadJNI { |
| public: |
| static LoggerThreadJNI& GetInstance() { |
| ATOMIC_STATIC(LoggerThreadJNI, instance); |
| return instance; |
| } |
| LoggerThreadJNI(); |
| ~LoggerThreadJNI(); |
| void SetFunc(JNIEnv* env, jobject func, jmethodID mid); |
| void Start(); |
| void Stop(); |
| |
| void Log(unsigned int level, const char* file, unsigned int line, |
| const char* msg); |
| |
| private: |
| void ThreadMain(); |
| |
| std::thread m_thread; |
| std::mutex m_mutex; |
| std::condition_variable m_cond; |
| std::atomic_bool m_active; |
| struct LogMessage { |
| LogMessage(unsigned int level_, const char* file_, unsigned int line_, |
| const char* msg_) |
| : level(level_), file(file_), line(line_), msg(msg_) {} |
| unsigned int level; |
| const char* file; |
| unsigned int line; |
| std::string msg; |
| }; |
| std::queue<LogMessage> m_queue; |
| std::mutex m_shutdown_mutex; |
| std::condition_variable m_shutdown_cv; |
| bool m_shutdown = false; |
| jobject m_func = nullptr; |
| jmethodID m_mid; |
| |
| ATOMIC_STATIC_DECL(LoggerThreadJNI) |
| }; |
| |
| ATOMIC_STATIC_INIT(LoggerThreadJNI) |
| |
| LoggerThreadJNI::LoggerThreadJNI() { |
| m_active = false; |
| } |
| |
| LoggerThreadJNI::~LoggerThreadJNI() { |
| Stop(); |
| } |
| |
| void LoggerThreadJNI::SetFunc(JNIEnv* env, jobject func, jmethodID mid) { |
| std::lock_guard<std::mutex> lock(m_mutex); |
| // free global reference |
| if (m_func) env->DeleteGlobalRef(m_func); |
| // create global reference |
| m_func = env->NewGlobalRef(func); |
| m_mid = mid; |
| } |
| |
| void LoggerThreadJNI::Start() { |
| { |
| std::lock_guard<std::mutex> lock(m_mutex); |
| if (m_active) return; |
| m_active = true; |
| } |
| { |
| std::lock_guard<std::mutex> lock(m_shutdown_mutex); |
| m_shutdown = false; |
| } |
| m_thread = std::thread(&LoggerThreadJNI::ThreadMain, this); |
| } |
| |
| void LoggerThreadJNI::Stop() { |
| { |
| std::lock_guard<std::mutex> lock(m_mutex); |
| if (!m_active) return; |
| m_active = false; |
| } |
| m_cond.notify_one(); // wake up thread |
| |
| // join threads, with timeout |
| if (m_thread.joinable()) { |
| std::unique_lock<std::mutex> lock(m_shutdown_mutex); |
| auto timeout_time = |
| std::chrono::steady_clock::now() + std::chrono::seconds(1); |
| if (m_shutdown_cv.wait_until(lock, timeout_time, |
| [&] { return m_shutdown; })) |
| m_thread.join(); |
| else |
| m_thread.detach(); // timed out, detach it |
| } |
| } |
| |
| void LoggerThreadJNI::Log(unsigned int level, const char *file, |
| unsigned int line, const char *msg) { |
| std::lock_guard<std::mutex> lock(m_mutex); |
| if (!m_active) return; |
| m_queue.emplace(level, file, line, msg); |
| m_cond.notify_one(); |
| } |
| |
| void LoggerThreadJNI::ThreadMain() { |
| JNIEnv *env; |
| jint rs = jvm->AttachCurrentThread((void**)&env, NULL); |
| if (rs != JNI_OK) return; |
| |
| std::unique_lock<std::mutex> lock(m_mutex); |
| while (m_active) { |
| m_cond.wait(lock, [&] { return !m_active || !m_queue.empty(); }); |
| if (!m_active) break; |
| while (!m_queue.empty()) { |
| if (!m_active) break; |
| auto item = std::move(m_queue.front()); |
| m_queue.pop(); |
| auto func = m_func; |
| auto mid = m_mid; |
| lock.unlock(); // don't hold mutex during callback execution |
| env->CallVoidMethod(func, mid, (jint)item.level, |
| ToJavaString(env, item.file), (jint)item.line, |
| ToJavaString(env, item.msg)); |
| if (env->ExceptionCheck()) { |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| } |
| lock.lock(); |
| } |
| } |
| |
| if (jvm) jvm->DetachCurrentThread(); |
| |
| // use condition variable to signal thread shutdown |
| { |
| std::lock_guard<std::mutex> lock(m_shutdown_mutex); |
| m_shutdown = true; |
| m_shutdown_cv.notify_one(); |
| } |
| } |
| |
| extern "C" { |
| |
| /* |
| * Class: edu_wpi_first_wpilibj_networktables_NetworkTablesJNI |
| * Method: setLogger |
| * Signature: (Ledu/wpi/first/wpilibj/networktables/NetworkTablesJNI/LoggerFunction;I)V |
| */ |
| JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_networktables_NetworkTablesJNI_setLogger |
| (JNIEnv *env, jclass, jobject func, jint minLevel) |
| { |
| // cls is a temporary here; cannot be used within callback functor |
| jclass cls = env->GetObjectClass(func); |
| if (!cls) return; |
| |
| // method ids, on the other hand, are safe to retain |
| jmethodID mid = env->GetMethodID( |
| cls, "apply", "(ILjava/lang/String;ILjava/lang/String;)V"); |
| if (!mid) return; |
| |
| auto& thread = LoggerThreadJNI::GetInstance(); |
| thread.SetFunc(env, func, mid); |
| thread.Start(); |
| |
| nt::SetLogger( |
| [](unsigned int level, const char *file, unsigned int line, |
| const char *msg) { |
| LoggerThreadJNI::GetInstance().Log(level, file, line, msg); |
| }, |
| minLevel); |
| } |
| |
| } // extern "C" |