diff --git a/hal/include/HAL/Task.hpp b/hal/include/HAL/Task.hpp index 87d51d89ac..eb1ad00b11 100644 --- a/hal/include/HAL/Task.hpp +++ b/hal/include/HAL/Task.hpp @@ -37,14 +37,16 @@ typedef int32_t TASK; typedef pthread_t* TASK; #endif -extern "C" -{ - extern const uint32_t VXWORKS_FP_TASK; - extern const int32_t HAL_objLib_OBJ_ID_ERROR; - extern const int32_t HAL_objLib_OBJ_DELETED; - extern const int32_t HAL_taskLib_ILLEGAL_OPTIONS; - extern const int32_t HAL_memLib_NOT_ENOUGH_MEMORY; - extern const int32_t HAL_taskLib_ILLEGAL_PRIORITY; +extern "C" { + // Note: These constants used to be declared extern and were defined in + // Task.cpp. This caused issues with the JNI bindings for java, and so the + // instantiations were moved here. + const uint32_t VXWORKS_FP_TASK = 0x01000000; + const int32_t HAL_objLib_OBJ_ID_ERROR = -1; // TODO: update to relevant TaskIDError + const int32_t HAL_objLib_OBJ_DELETED = -1; // TODO: update to relevant TaskDeletedError + const int32_t HAL_taskLib_ILLEGAL_OPTIONS = -1; // TODO: update to relevant TaskOptionsError + const int32_t HAL_memLib_NOT_ENOUGH_MEMORY = -1; // TODO: update to relevant TaskMemoryError + const int32_t HAL_taskLib_ILLEGAL_PRIORITY = -1; // TODO: update to relevant TaskPriorityError TASK spawnTask(char * name, int priority, int options, int stackSize, FUNCPTR entryPt, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, @@ -58,5 +60,5 @@ extern "C" STATUS verifyTaskID(TASK task); STATUS setTaskPriority(TASK task, int priority); STATUS getTaskPriority(TASK task, int* priority); -} +} diff --git a/hal/lib/Athena/Task.cpp b/hal/lib/Athena/Task.cpp index f509c28e77..0777f89e36 100644 --- a/hal/lib/Athena/Task.cpp +++ b/hal/lib/Athena/Task.cpp @@ -4,12 +4,6 @@ #include #include -const uint32_t VXWORKS_FP_TASK = 0x01000000; -const int32_t HAL_objLib_OBJ_ID_ERROR = -1; // TODO: update to relevant TaskIDError -const int32_t HAL_objLib_OBJ_DELETED = -1; // TODO: update to relevant TaskDeletedError -const int32_t HAL_taskLib_ILLEGAL_OPTIONS = -1; // TODO: update to relevant TaskOptionsError -const int32_t HAL_memLib_NOT_ENOUGH_MEMORY = -1; // TODO: update to relevant TaskMemoryError -const int32_t HAL_taskLib_ILLEGAL_PRIORITY = -1; // TODO: update to relevant TaskPriorityError struct TaskArgs { FUNCPTR fun; @@ -30,7 +24,7 @@ void* startRoutine(void* data) { return ret; } -TASK spawnTask(char * name, int priority, int options, int stackSize, +TASK spawnTask(char * name, int priority, int options, int stackSize, FUNCPTR entryPt, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8, uint32_t arg9) { diff --git a/wpilibj/build.gradle b/wpilibj/build.gradle index 3872195103..bb027daca7 100644 --- a/wpilibj/build.gradle +++ b/wpilibj/build.gradle @@ -245,6 +245,7 @@ task jniHeaders { args 'edu.wpi.first.wpilibj.hal.EncoderJNI' args 'edu.wpi.first.wpilibj.hal.I2CJNI' args 'edu.wpi.first.wpilibj.hal.InterruptJNI' + args 'edu.wpi.first.wpilibj.hal.NotifierJNI' args 'edu.wpi.first.wpilibj.hal.PWMJNI' args 'edu.wpi.first.wpilibj.hal.RelayJNI' args 'edu.wpi.first.wpilibj.hal.SPIJNI' diff --git a/wpilibj/wpilibJavaDevices/build.gradle b/wpilibj/wpilibJavaDevices/build.gradle index c93ed721a8..bf729fbfdf 100644 --- a/wpilibj/wpilibJavaDevices/build.gradle +++ b/wpilibj/wpilibJavaDevices/build.gradle @@ -29,6 +29,7 @@ task jniHeaders { args 'edu.wpi.first.wpilibj.hal.EncoderJNI' args 'edu.wpi.first.wpilibj.hal.I2CJNI' args 'edu.wpi.first.wpilibj.hal.InterruptJNI' + args 'edu.wpi.first.wpilibj.hal.NotifierJNI' args 'edu.wpi.first.wpilibj.hal.PWMJNI' args 'edu.wpi.first.wpilibj.hal.RelayJNI' args 'edu.wpi.first.wpilibj.hal.SPIJNI' @@ -58,4 +59,5 @@ dependencies { compile 'junit:junit:4.11' compile project(':networktables:java') compile project(':wpilibj:wpilibJava') + compile project(':hal') } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Notifier.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Notifier.java new file mode 100644 index 0000000000..f92bf78534 --- /dev/null +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/Notifier.java @@ -0,0 +1,291 @@ +package edu.wpi.first.wpilibj; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.lang.Runtime; + +import edu.wpi.first.wpilibj.hal.HALUtil; +import edu.wpi.first.wpilibj.hal.NotifierJNI; +import edu.wpi.first.wpilibj.Utility; + +public class Notifier { + + static private class ProcessQueue implements Runnable { + public void run() { + Notifier current; + while (true) { + Notifier.queueLock.lock(); + + double currentTime = Utility.getFPGATime() * 1e-6; + current = Notifier.timerQueueHead; + + if (current == null || current.m_expirationTime > currentTime) { + Notifier.queueLock.unlock(); + break; + } + + Notifier.timerQueueHead = current.m_nextEvent; + + if (current.m_periodic) { + current.insertInQueue(true); + } + else { + current.m_queued = false; + } + + current.m_handlerLock.lock(); + Notifier.queueLock.unlock(); + + current.m_handler.run(); + current.m_handlerLock.unlock(); + } + + + Notifier.queueLock.lock(); + Notifier.updateAlarm(); + Notifier.queueLock.unlock(); + } + + } + + // Maximum time, in seconds, that the FPGA returns before rolling over to 0. + static private final double kRolloverTime = (1l << 32) / 1e6; + // Number of instances of Notifier classes created, so that we can call + // cleanNotifier() after all the Notifiers are stopped. + static private int refcount = 0; + // The next Notifier instance which needs to be called. + static private Notifier timerQueueHead = null; + // The C pointer to the notifier object. We don't use it directly, it is just + // passed to the JNI bindings. + private static ByteBuffer m_notifier; + // The lock for the queue information (namely, timerQueueHead and the + // m_nextEvent members). + private static ReentrantLock queueLock = new ReentrantLock(); + // The handler which is called by the HAL library; it handles the subsequent + // calling of the user handlers. + // This is the only Runnable actually passed to the JNI bindings. + private static ProcessQueue m_processQueue; + + // The next Notifier whose handler will need to be called after this one. + private Notifier m_nextEvent = null; + // The time, in microseconds, at which the corresponding handler should be + // called. Has the same zero as Utility.getFPGATime(). + private double m_expirationTime = 0; + // The handler passed in by the user which should be called at the appropriate + // interval. + private Runnable m_handler; + // Whether we are calling the handler just once or periodically. + private boolean m_periodic = false; + // If periodic, the period of the calling; if just once, stores how long it + // is until we call the handler. + private double m_period = 0; + // Whether we are currently queued to be called at m_expirationTime. + private boolean m_queued = false; + // Lock on the handler so that the handler is not called before it has + // completed. This is only relevant if the handler takes a very long time to + // complete (or the period is very short) and when everything is being + // destructed. + private ReentrantLock m_handlerLock = new ReentrantLock(); + + /** + * This is done to store the JVM variable in the InterruptJNI + * This is done because the HAL must have access to the JVM variable + * in order to attach the newly spawned thread when an interrupt is fired. + */ + static { + ByteBuffer status = pointer(); + NotifierJNI.initializeNotifierJVM(status.asIntBuffer()); + HALUtil.checkStatus(status.asIntBuffer()); + } + + /** + * Create a Notifier for timer event notification. + * @param handler The handler is called at the notification time which is set + * using StartSingle or StartPeriodic. + */ + public Notifier(Runnable run) { + if (refcount == 0) { + init(); + } + refcount += 1; + m_handler = run; + } + + protected void finalize() { + queueLock.lock(); + + deleteFromQueue(); + + // If this was the last instance of a Notifier, clean up after ourselves. + if ((--refcount) == 0) { + ByteBuffer status = pointer(); + NotifierJNI.cleanNotifier(m_notifier, status.asIntBuffer()); + HALUtil.checkStatus(status.asIntBuffer()); + } + + queueLock.unlock(); + + m_handlerLock.lock(); + m_handlerLock = null; + } + + /** + * Update the alarm hardware to reflect the current first element in the + * queue. + * Compute the time the next alarm should occur based on the current time and + * the period for the first element in the timer queue. + * WARNING: this method does not do synchronization! It must be called from + * somewhere that is taking care of synchronizing access to the queue. + */ + static protected void updateAlarm() { + if (timerQueueHead != null) { + ByteBuffer status = pointer(); + NotifierJNI.updateNotifierAlarm( + m_notifier, (int)(timerQueueHead.m_expirationTime * 1e6), + status.asIntBuffer()); + HALUtil.checkStatus(status.asIntBuffer()); + } + } + + /** + * Insert this Notifier into the timer queue in right place. + * WARNING: this method does not do synchronization! It must be called from + * somewhere that is taking care of synchronizing access to the queue. + * @param reschedule If false, the scheduled alarm is based on the current + * time and UpdateAlarm method is called which will enable the alarm if + * necessary. If true, update the time by adding the period (no drift) when + * rescheduled periodic from ProcessQueue. This ensures that the public + * methods only update the queue after finishing inserting. + */ + protected void insertInQueue(boolean reschedule) { + if (reschedule) { + m_expirationTime += m_period; + } + else { + m_expirationTime = Utility.getFPGATime() * 1e-6 + m_period; + } + + if (m_expirationTime > kRolloverTime) { + m_expirationTime -= kRolloverTime; + } + + if (timerQueueHead == null || timerQueueHead.m_expirationTime >= this.m_expirationTime) { + // the queue is empty or greater than the new entry + // the new entry becomes the first element + this.m_nextEvent = timerQueueHead; + timerQueueHead = this; + + if (!reschedule) { + // since the first element changed, update alarm, unless we already plan to + updateAlarm(); + } + } + else { + for (Notifier n = timerQueueHead; ; n = n.m_nextEvent) { + if (n.m_nextEvent == null || + n.m_nextEvent.m_expirationTime > this.m_expirationTime) { + this.m_nextEvent = n.m_nextEvent; + n.m_nextEvent = this; + break; + } + } + } + + m_queued = true; + } + + /** + * Delete this Notifier from the timer queue. + * WARNING: this method does not do synchronization! It must be called from + * somewhere that is taking care of synchronizing access to the queue. + * Remove this Notifier from the timer queue and adjust the next interrupt + * time to reflect the current top of the queue. + */ + private void deleteFromQueue() { + if (m_queued) { + m_queued = false; + assert(timerQueueHead != null); + if (timerQueueHead == this) { + // removing the first item in the list - update the alarm + timerQueueHead = this.m_nextEvent; + updateAlarm(); + } + else { + for (Notifier n = timerQueueHead; n != null; n = n.m_nextEvent) { + if (n.m_nextEvent == this) { + // this element is the next element from *n from the queue + // Point n around this. + n.m_nextEvent = this.m_nextEvent; + } + } + } + } + } + + /** + * Register for single event notification. + * A timer event is queued for a single event after the specified delay. + * @param delay Seconds to wait before the handler is called. + */ + public void startSingle(double delay) { + queueLock.lock(); + m_periodic = false; + m_period = delay; + deleteFromQueue(); + insertInQueue(false); + queueLock.unlock(); + } + + /** + * Register for periodic event notification. + * A timer event is queued for periodic event notification. Each time the + * interrupt occurs, the event will be immediately requeued for the same time + * interval. + * @param period Period in seconds to call the handler starting one period + * after the call to this method. + */ + public void startPeriodic(double period) { + queueLock.lock(); + m_periodic = true; + m_period = period; + deleteFromQueue(); + insertInQueue(false); + queueLock.unlock(); + } + + /** + * Stop timer events from occuring. + * Stop any repeating timer events from occuring. This will also remove any + * single notification events from the queue. + * If a timer-based call to the registered handler is in progress, this + * function will block until the handler call is complete. + */ + public void stop() { + queueLock.lock(); + deleteFromQueue(); + queueLock.unlock(); + + // Wait for a currently executing handler to complete before returning from + // stop() + m_handlerLock.lock(); + m_handlerLock.unlock(); + } + + // First time init. + protected static void init() { + ByteBuffer status = pointer(); + m_processQueue = new ProcessQueue(); + m_notifier = NotifierJNI.initializeNotifier(m_processQueue, status.asIntBuffer()); + HALUtil.checkStatus(status.asIntBuffer()); + } + + // Returns a ByteBuffer with the appropriate length and endianness to pass to + // the JNI bindings. + protected static ByteBuffer pointer() { + ByteBuffer buf = ByteBuffer.allocateDirect(HALUtil.pointerSize()); + buf.order(ByteOrder.LITTLE_ENDIAN); + return buf; + } +} diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java index bdb2e47cd9..4f82bceb30 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java @@ -32,7 +32,7 @@ public class HALUtil extends JNIWrapper { public static native boolean getFPGAButton(IntBuffer status); public static native String getHALErrorMessage(int code); - + public static native int getHALErrno(); public static native String getHALstrerror(int errno); public static String getHALstrerror(){ @@ -52,4 +52,5 @@ public class HALUtil extends JNIWrapper { } } + public static native int pointerSize(); } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/NotifierJNI.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/NotifierJNI.java new file mode 100644 index 0000000000..c9c3dbc95f --- /dev/null +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/NotifierJNI.java @@ -0,0 +1,35 @@ +package edu.wpi.first.wpilibj.hal; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.lang.Runtime; + +/** + * The NotifierJNI class directly wraps the C++ HAL Notifier. + * + * This class is not meant for direct use by teams. Instead, the + * edu.wpi.first.wpilibj.Notifier class, which corresponds to the C++ Notifier + * class, should be used. + */ +public class NotifierJNI extends JNIWrapper { + /** + * Initializes the notifier to call the run() function of a Runnable. + * + * Should be called after initializeNotifierJVM(). + */ + public static native ByteBuffer initializeNotifier(Runnable func, IntBuffer status); + /** + * Initializes the JVM for use by the callback. Should be called before + * anything else. + */ + public static native void initializeNotifierJVM(IntBuffer status); + /** + * Deletes the notifier object when we are done with it. + */ + public static native void cleanNotifier(ByteBuffer notifierPtr, IntBuffer status); + /** + * Sets the notifier to call the callback in another triggerTime microseconds. + */ + public static native void updateNotifierAlarm(ByteBuffer notifierPtr, + int triggerTime, IntBuffer status); +} diff --git a/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp b/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp index badc0a145b..5a613be155 100644 --- a/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp +++ b/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp @@ -214,3 +214,8 @@ JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_hal_HALUtil_getHALstrerror HALUTIL_LOG(logDEBUG) << "Calling HALUtil getHALstrerror errorCode=" << errorCode << " msg=" << msg; return env->NewStringUTF(msg); } + +JNIEXPORT jint JNICALL + Java_edu_wpi_first_wpilibj_hal_HALUtil_pointerSize(JNIEnv*, jclass) { + return sizeof(void*); +} diff --git a/wpilibj/wpilibJavaJNI/lib/NotifierJNI.cpp b/wpilibj/wpilibJavaJNI/lib/NotifierJNI.cpp new file mode 100644 index 0000000000..273d8fd2ab --- /dev/null +++ b/wpilibj/wpilibJavaJNI/lib/NotifierJNI.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include "Log.hpp" +#include "edu_wpi_first_wpilibj_hal_NotifierJNI.h" +#include "HAL/Notifier.hpp" + +// set the logging level +TLogLevel notifierJNILogLevel = logWARNING; + +#define NOTIFIERJNI_LOG(level) \ + if (level > notifierJNILogLevel) ; \ + else Log().Get(level) + +// The jvm object is necessary in order to attach new threads (ie, +// notifierHandler), to the JVM. +static JavaVM *jvm; + +static const int kPtrSize = sizeof(void*); + +// Utility functions which convert back and forth between pointers and Java +// ByteBuffers. + +jint* GetStatusPtr(JNIEnv *env, jobject status) { + return (jint*)env->GetDirectBufferAddress(status); +} + +jobject PtrToByteBuf(JNIEnv *env, void *ptr) { + // Stores a pointer into a byte buffer of the appropriate length. + return env->NewDirectByteBuffer(ptr, kPtrSize); +} + +void *ByteBufToPtr(JNIEnv *env, jobject bytebuf) { + void * ptr = (void*)env->GetDirectBufferAddress(bytebuf); + return ptr; +} + +// These two are used to pass information to the notifierHandler without using +// up function parameters. +// See below for more information. +static jobject func_global; +static jmethodID mid_global; + +// The arguments are unused by the HAL Notifier; they just satisfy a particular +// function signature. +void notifierHandler(uint32_t mask, void* param) { + jobject handler_obj = func_global; + jmethodID mid = mid_global; + + NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI interruptHandler"; + + //Because this is a callback in a new thread we must attach it to the JVM. + JNIEnv *env; + jint rs; + // Check to see if we are already part of a JVM thread or if we need to attach + // ourselves. + int getEnvStat = jvm->GetEnv((void **)&env, JNI_VERSION_1_8); + if (getEnvStat == JNI_EDETACHED) { + rs = jvm->AttachCurrentThread((void**)&env, NULL); + assert (rs == JNI_OK); + } + NOTIFIERJNI_LOG(logDEBUG) << "Attached to thread. Object is: " << handler_obj; + + // Actuall call the user function. + env->CallVoidMethod(handler_obj, mid); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + } + + // Only detach if we needed to attach oursleves in the first place. + if (getEnvStat == JNI_EDETACHED) { + rs = jvm->DetachCurrentThread(); + assert (rs == JNI_OK); + } + NOTIFIERJNI_LOG(logDEBUG) << "Leaving NOTIFIERJNI interruptHandler"; +} + +/* + * Class: edu_wpi_first_wpilibj_hal_NotifierJNI + * Method: initializeNotifierJVM + * Signature: (Ljava/nio/IntBuffer;)V + */ +JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_initializeNotifierJVM + (JNIEnv *env, jclass, jobject status) +{ + //This method should be called once to setup the JVM + NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI initializeNotifierJVM"; + jint * statusPtr = GetStatusPtr(env, status); + NOTIFIERJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr; + jint rs = env->GetJavaVM(&jvm); + assert (rs == JNI_OK); +} + +/* + * Class: edu_wpi_first_wpilibj_hal_NotifierJNI + * Method: initializeNotifier + * Signature: (Ljava/lang/Runnable;Ljava/nio/IntBuffer;)Ljava/lang/ByteBuffer; + */ +JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_hal_NotifierJNI_initializeNotifier + (JNIEnv *env, jclass, jobject func, jobject status) +{ + NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI initializeNotifier"; + jint * statusPtr = GetStatusPtr(env, status); + NOTIFIERJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr; + + jclass cls = env->GetObjectClass(func); + jmethodID mid = env->GetMethodID(cls, "run", "()V"); + + // In order to pass the user's Runnable to the notifierHandler, we have to use + // something other than the function arguments (because the function arguments + // are dictated by the callback format). As such, we instead use a couple of + // global variables to pass around the object reference. + // This is not ideal, but the only other option that came to mind was to use + // lambda function captures, but, unfortunately, it turns out that using + // captures in a lambda changes the function signature such that it can no + // long be used as a standared C-style function pointer. + // Need to set as global ref to avoid seg faults when referring to it later. + func_global = env->NewGlobalRef(func); + mid_global = mid; + + void *notifierPtr = initializeNotifier(notifierHandler, statusPtr); + + NOTIFIERJNI_LOG(logDEBUG) << "Notifier Ptr = " << notifierPtr; + NOTIFIERJNI_LOG(logDEBUG) << "Status = " << *statusPtr; + + return PtrToByteBuf(env, notifierPtr); +} + +/* + * Class: edu_wpi_first_wpilibj_hal_NotifierJNI + * Method: cleanNotifier + * Signature: (Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)V + */ +JNIEXPORT void JNICALL + Java_edu_wpi_first_wpilibj_hal_NotifierJNI_cleanNotifier(JNIEnv *env, jclass, jobject notifierPtr, jobject status) { + NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI cleanNotifier"; + + void *ptr = ByteBufToPtr(env, notifierPtr); + NOTIFIERJNI_LOG(logDEBUG) << "Notifier Ptr = " << ptr; + + jint *statusPtr = GetStatusPtr(env, status); + NOTIFIERJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr; + + cleanNotifier(ptr, statusPtr); + NOTIFIERJNI_LOG(logDEBUG) << "Status = " << *statusPtr; +} + +/* + * Class: edu_wpi_first_wpilibj_hal_NotifierJNI + * Method: updateNotifierAlarm + * Signature: (Ljava/nio/ByteBuffer;ILjava/nio/IntBuffer;)V + */ +JNIEXPORT void JNICALL + Java_edu_wpi_first_wpilibj_hal_NotifierJNI_updateNotifierAlarm( + JNIEnv *env, jclass cls, jobject notifierPtr, jint triggerTime, + jobject status) { + NOTIFIERJNI_LOG(logDEBUG) << "Calling NOTIFIERJNI updateNotifierAlarm"; + + void *ptr = ByteBufToPtr(env, notifierPtr); + NOTIFIERJNI_LOG(logDEBUG) << "Notifier Ptr = " << ptr; + + jint *statusPtr = GetStatusPtr(env, status); + NOTIFIERJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr; + NOTIFIERJNI_LOG(logDEBUG) << "triggerTime Ptr = " << &triggerTime; + + updateNotifierAlarm(ptr, (uint32_t)triggerTime, statusPtr); + NOTIFIERJNI_LOG(logDEBUG) << "Status = " << *statusPtr; +}