Adds interrupts to Java

Implements the JNI bindings for java
Adds integration tests for Digital Inputs and AnalogTriggers.
Adds the ability to get the value and message from errno in java using the HALUtil JNI class.

Change-Id: I853529fdab9744ce95ee15d4cc73dc3953265552
This commit is contained in:
Jonathan Leitschuh
2014-08-04 14:19:01 -04:00
parent 23b6a980c2
commit 8ba0eada17
18 changed files with 834 additions and 213 deletions

View File

@@ -6,6 +6,9 @@
#include <stdint.h>
#endif
#include <iostream>
#include "errno.h"
extern "C"
{
typedef void (*InterruptHandlerFunction)(uint32_t interruptAssertedMask, void *param);

View File

@@ -52,8 +52,10 @@ void enableInterrupts(void* interrupt_pointer, int32_t *status)
*/
void disableInterrupts(void* interrupt_pointer, int32_t *status)
{
Interrupt* anInterrupt = (Interrupt*)interrupt_pointer;
anInterrupt->manager->disable(status);
}
/**

View File

@@ -92,6 +92,7 @@ public class AnalogTriggerOutput extends DigitalSource {
@Override
public void free() {
}
/**
@@ -113,8 +114,8 @@ public class AnalogTriggerOutput extends DigitalSource {
}
@Override
public int getModuleForRouting() {
return m_trigger.m_index >> 2;
public byte getModuleForRouting() {
return (byte) (m_trigger.m_index >> 2);
}
@Override
@@ -122,27 +123,6 @@ public class AnalogTriggerOutput extends DigitalSource {
return true;
}
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* the interrupt service routine
* @param param
* a parameter for the ISR
*/
// public void requestInterrupts(/*tInterruptHandler*/Object handler, Object
// param) {
// TODO: add interrupt support
// TODO: throw exception
// }
/**
* Request interrupts synchronously on this digital input.
*/
// public void requestInterrupts() {
// TODO: throw exception
// }
/**
* Defines the state in which the AnalogTrigger triggers
* @author jonathanleitschuh

View File

@@ -12,14 +12,10 @@ import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptHandlerFunction;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.util.AllocationException;
import edu.wpi.first.wpilibj.util.CheckedAllocationException;
/**
* Class to read a digital input. This class will read digital inputs and return
@@ -67,97 +63,15 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
return m_channel;
}
@Override
public boolean getAnalogTriggerForRouting() {
return false;
}
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* The address of the interrupt handler function of type
* tInterruptHandler that will be called whenever there is an
* interrupt on the digitial input port. Request interrupts in
* synchronus mode where the user program interrupt handler will
* be called when an interrupt occurs. The default is interrupt
* on rising edges only.
*/
public void requestInterrupts(InterruptHandlerFunction handler) {
// TODO: add interrupt support
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
allocateInterrupts(false);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, (byte) getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
setUpSourceEdge(true, false);
InterruptJNI.attachInterruptHandler(m_interrupt, handler, null, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Request interrupts synchronously on this digital input. Request
* interrupts in synchronus mode where the user program will have to
* explicitly wait for the interrupt to occur. The default is interrupt on
* rising edges only.
*/
public void requestInterrupts() {
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
allocateInterrupts(true);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, (byte) getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
}
/**
* Set which edge to trigger interrupts on
*
* @param risingEdge
* true to interrupt on rising edge
* @param fallingEdge
* true to interrupt on falling edge
*/
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
if (m_interrupt != null) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.setInterruptUpSourceEdge(m_interrupt,
(byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0),
status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
} else {
throw new IllegalArgumentException(
"You must call RequestInterrupts before setUpSourceEdge");
}
}
/*
* Live Window code, only does anything if live window is activated.
*/
@Override
public String getSmartDashboardType() {
return "Digital Input";
}
@@ -167,6 +81,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void initTable(ITable subtable) {
m_table = subtable;
updateTable();
@@ -175,6 +90,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void updateTable() {
if (m_table != null) {
m_table.putBoolean("Value", get());
@@ -184,6 +100,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public ITable getTable() {
return m_table;
}
@@ -191,12 +108,14 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void startLiveWindowMode() {
}
/**
* {@inheritDoc}
*/
@Override
public void stopLiveWindowMode() {
}
}

View File

@@ -7,24 +7,21 @@
package edu.wpi.first.wpilibj;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
//import com.sun.jna.Pointer;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.PWMJNI;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.hal.PWMJNI;
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
//import com.sun.jna.Pointer;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
/**
* Class to write digital outputs. This class will wrtie digital outputs. Other
* Class to write digital outputs. This class will write digital outputs. Other
* devices that are implemented elsewhere will automatically allocate digital
* inputs and outputs as required.
*/
@@ -48,6 +45,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* Free the resources associated with a digital output.
*/
@Override
public void free() {
// disable the pwm only if we have allocated it
if (m_pwmGenerator != null) {
@@ -104,6 +102,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
* @param pulseLength
* The length of the pulse.
*/
@Deprecated
public void pulse(final int channel, final int pulseLength) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
@@ -212,6 +211,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/*
* Live Window code, only does anything if live window is activated.
*/
@Override
public String getSmartDashboardType() {
return "Digital Output";
}
@@ -222,6 +222,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void initTable(ITable subtable) {
m_table = subtable;
updateTable();
@@ -230,6 +231,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public ITable getTable() {
return m_table;
}
@@ -237,6 +239,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void updateTable() {
// TODO: Put current value.
}
@@ -244,8 +247,10 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void startLiveWindowMode() {
m_table_listener = new ITableListener() {
@Override
public void valueChanged(ITable itable, String key, Object value,
boolean bln) {
set(((Boolean) value).booleanValue());
@@ -257,6 +262,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void stopLiveWindowMode() {
// TODO: Broken, should only remove the listener from "Value" only.
m_table.removeTableListener(m_table_listener);

View File

@@ -7,9 +7,8 @@
package edu.wpi.first.wpilibj;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.HALUtil;
@@ -54,6 +53,7 @@ public abstract class DigitalSource extends InterruptableSensorBase {
HALUtil.checkStatus(status.asIntBuffer());
}
@Override
public void free() {
channels.free(m_channel);
ByteBuffer status = ByteBuffer.allocateDirect(4);
@@ -69,6 +69,7 @@ public abstract class DigitalSource extends InterruptableSensorBase {
*
* @return channel routing number
*/
@Override
public int getChannelForRouting() {
return m_channel;
}
@@ -78,15 +79,16 @@ public abstract class DigitalSource extends InterruptableSensorBase {
*
* @return 0
*/
public int getModuleForRouting() {
@Override
public byte getModuleForRouting() {
return 0;
}
/**
* Is this an analog trigger
*
* @return true if this is an analog trigger
*/
@Override
public boolean getAnalogTriggerForRouting() {
return false;
}

View File

@@ -0,0 +1,57 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2014. 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. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptJNIHandlerFunction;
/**
* It is recommended that you use this class in conjunction with classes from
* {@link java.util.concurrent.atomic} as these objects are all thread safe.
*
* @author Jonathan Leitschuh
*
* @param <T> The type of the parameter that should be returned to the the
* method {@link #interruptFired(int, Object)}
*/
public abstract class InterruptHandlerFunction<T>{
/**
* The entry point for the interrupt. When the interrupt fires the
* {@link #apply(int, Object)} method is called.
* The outer class is provided as an interface to allow the implementer to
* pass a generic object to the interrupt fired method.
* @author Jonathan Leitschuh
*/
private class Function implements InterruptJNIHandlerFunction{
@SuppressWarnings("unchecked")
@Override
public void apply(int interruptAssertedMask, Object param) {
interruptFired(interruptAssertedMask, (T)param);
}
}
final Function function = new Function();
/**
* This method is run every time an interrupt is fired.
* @param interruptAssertedMask
* @param param The parameter provided by overriding the {@link #overridableParamater()}
* method.
*/
abstract void interruptFired(int interruptAssertedMask, T param);
/**
* Override this method if you would like to pass a specific
* parameter to the {@link #interruptFired(int, Object)} when it is fired by the interrupt.
* This method is called once when {@link InterruptableSensorBase#requestInterrupts(InterruptHandlerFunction)}
* is run.
* @return The object that should be passed to the interrupt when it runs
*/
public T overridableParamater(){
return null;
}
}

View File

@@ -7,26 +7,40 @@
package edu.wpi.first.wpilibj;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptHandlerFunction;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.util.AllocationException;
import edu.wpi.first.wpilibj.util.CheckedAllocationException;
/**
* Base for sensors to be used with interrupts
*/
public abstract class InterruptableSensorBase extends SensorBase {
/**
* 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 = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.initializeInterruptJVM(status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* The interrupt resource
*/
protected ByteBuffer m_interrupt;
protected ByteBuffer m_interrupt = null;
/**
* The interrupt manager resource
* Flags if the interrupt being allocated is synchronous
*/
protected InterruptHandlerFunction m_manager;
protected boolean m_isSynchronousInterrupt = false;
/**
* The index of the interrupt
*/
@@ -40,25 +54,102 @@ public abstract class InterruptableSensorBase extends SensorBase {
* Create a new InterrupatableSensorBase
*/
public InterruptableSensorBase() {
m_manager = null;
m_interrupt = null;
}
/**
* @return
*/
abstract boolean getAnalogTriggerForRouting();
/**
* @return
*/
abstract int getChannelForRouting();
/**
* @return
*/
abstract byte getModuleForRouting();
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* The {@link #InterruptHandlerFunction} that contains the method
* {@link InterruptHandlerFunction#interruptFired(int, Object)} that
* will be called whenever there is an interrupt on this device.
* Request interrupts in synchronus mode where the user program
* interrupt handler will be called when an interrupt occurs. The
* default is interrupt on rising edges only.
*/
public void requestInterrupts(InterruptHandlerFunction<?> handler) {
if(m_interrupt != null){
throw new AllocationException("The interrupt has already been allocated");
}
allocateInterrupts(false);
assert (m_interrupt != null);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
InterruptJNI.attachInterruptHandler(m_interrupt, handler.function, handler.overridableParamater(), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Request interrupts synchronously on this digital input. Request
* interrupts in synchronous mode where the user program will have to
* explicitly wait for the interrupt to occur. The default is interrupt on
* rising edges only.
*/
public void requestInterrupts() {
if(m_interrupt != null){
throw new AllocationException("The interrupt has already been allocated");
}
allocateInterrupts(true);
assert (m_interrupt != null);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
}
/**
* Allocate the interrupt
*
* @param watcher
* @param watcher true if the interrupt should be in synchronous mode where the user
* program will have to explicitly wait for the interrupt to occur.
*/
public void allocateInterrupts(boolean watcher) {
if (!watcher) {
throw new IllegalArgumentException(
"Interrupt callbacks not yet supported");
protected void allocateInterrupts(boolean watcher) {
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
// Expects the calling leaf class to allocate an interrupt index.
IntBuffer status = IntBuffer.allocate(1);
m_isSynchronousInterrupt = watcher;
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
m_interrupt = InterruptJNI.initializeInterrupts(m_interruptIndex,
(byte) (watcher ? 1 : 0), status);
HALUtil.checkStatus(status);
(byte) (watcher ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -66,13 +157,15 @@ public abstract class InterruptableSensorBase extends SensorBase {
* structures and disables any interrupts.
*/
public void cancelInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.cleanInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.cleanInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
m_interrupt = null;
interrupts.free(m_interruptIndex);
}
/**
@@ -82,12 +175,13 @@ public abstract class InterruptableSensorBase extends SensorBase {
* Timeout in seconds
*/
public void waitForInterrupt(double timeout) {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.waitForInterrupt(m_interrupt, (float) timeout, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.waitForInterrupt(m_interrupt, (float) timeout, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -96,24 +190,32 @@ public abstract class InterruptableSensorBase extends SensorBase {
* other options before starting to field interrupts.
*/
public void enableInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.enableInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
if(m_isSynchronousInterrupt){
throw new IllegalStateException("You do not need to enable synchronous interrupts");
}
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.enableInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Disable Interrupts without without deallocating structures.
*/
public void disableInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.disableInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
if(m_isSynchronousInterrupt){
throw new IllegalStateException("You can not disable synchronous interrupts");
}
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.disableInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -123,12 +225,36 @@ public abstract class InterruptableSensorBase extends SensorBase {
* @return Timestamp in seconds since boot.
*/
public double readInterruptTimestamp() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
double timestamp = InterruptJNI.readInterruptTimestamp(m_interrupt, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
double timestamp = InterruptJNI.readInterruptTimestamp(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
return timestamp;
}
/**
* Set which edge to trigger interrupts on
*
* @param risingEdge
* true to interrupt on rising edge
* @param fallingEdge
* true to interrupt on falling edge
*/
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
if (m_interrupt != null) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.setInterruptUpSourceEdge(m_interrupt,
(byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0),
status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
} else {
throw new IllegalArgumentException(
"You must call RequestInterrupts before setUpSourceEdge");
}
}
}

View File

@@ -1,7 +1,7 @@
package edu.wpi.first.wpilibj.hal;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class HALUtil extends JNIWrapper {
public static final int NULL_PARAMETER = -5;
@@ -28,6 +28,12 @@ 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(){
return getHALstrerror(getHALErrno());
}
public static void checkStatus(IntBuffer status)
{

View File

@@ -1,12 +1,13 @@
package edu.wpi.first.wpilibj.hal;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class InterruptJNI extends JNIWrapper {
public interface InterruptHandlerFunction {
void apply(int interruptAssertedMask, ByteBuffer param);
public interface InterruptJNIHandlerFunction {
void apply(int interruptAssertedMask, Object param);
};
public static native void initializeInterruptJVM(IntBuffer status);
public static native ByteBuffer initializeInterrupts(int interruptIndex, byte watcher, IntBuffer status);
public static native void cleanInterrupts(ByteBuffer interrupt_pointer, IntBuffer status);
public static native void waitForInterrupt(ByteBuffer interrupt_pointer, double timeout, IntBuffer status);
@@ -14,6 +15,6 @@ public class InterruptJNI extends JNIWrapper {
public static native void disableInterrupts(ByteBuffer interrupt_pointer, IntBuffer status);
public static native double readInterruptTimestamp(ByteBuffer interrupt_pointer, IntBuffer status);
public static native void requestInterrupts(ByteBuffer interrupt_pointer, byte routing_module, int routing_pin, byte routing_analog_trigger, IntBuffer status);
public static native void attachInterruptHandler(ByteBuffer interrupt_pointer, InterruptHandlerFunction handler, ByteBuffer param, IntBuffer status);
public static native void attachInterruptHandler(ByteBuffer interrupt_pointer, InterruptJNIHandlerFunction handler, Object param, IntBuffer status);
public static native void setInterruptUpSourceEdge(ByteBuffer interrupt_pointer, byte risingEdge, byte fallingEdge, IntBuffer status);
}

View File

@@ -0,0 +1,239 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2014. 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. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Test;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
/**
* This class should not be run as a test explicitly. Instead it should be extended by tests that use the InterruptableSensorBase
*
* @author jonathanleitschuh
*
*/
public abstract class AbstractInterruptTest extends AbstractComsSetup {
private InterruptableSensorBase interruptable = null;
private InterruptableSensorBase getInterruptable(){
if(interruptable == null){
interruptable = giveInterruptableSensorBase();
}
return interruptable;
}
@After
public void interruptTeardown(){
if(interruptable != null){
freeInterruptableSensorBase();
interruptable = null;
}
}
/**
* Give the interruptible sensor base that interrupts can be attached to.
* @return
*/
abstract InterruptableSensorBase giveInterruptableSensorBase();
/**
* Cleans up the interruptible sensor base. This is only called if {@link #giveInterruptableSensorBase()} is called.
*/
abstract void freeInterruptableSensorBase();
/**
* Perform whatever action is required to set the interrupt high.
*/
abstract void setInterruptHigh();
/**
* Perform whatever action is required to set the interrupt low.
*/
abstract void setInterruptLow();
private class InterruptCounter{
private final AtomicInteger count = new AtomicInteger();
void increment(){
count.addAndGet(1);
}
int getCount(){
return count.get();
}
};
private class TestInterruptHandlerFunction extends InterruptHandlerFunction<InterruptCounter>{
protected final AtomicBoolean exceptionThrown = new AtomicBoolean(false);
/** Stores the time that the interrupt fires */
final AtomicLong interruptFireTime = new AtomicLong();
/** Stores if the interrupt has completed at least once */
final AtomicBoolean interruptComplete = new AtomicBoolean(false);
protected Exception ex;
final InterruptCounter counter;
TestInterruptHandlerFunction(InterruptCounter counter){
this.counter = counter;
}
@Override
void interruptFired(int interruptAssertedMask, InterruptCounter param) {
interruptFireTime.set(Utility.getFPGATime());
counter.increment();
try{
//This won't cause the test to fail
assertSame(counter, param);
} catch (Exception ex){
//So we must throw the exception within the test
exceptionThrown.set(true);
this.ex = ex;
}
interruptComplete.set(true);
};
@Override
public InterruptCounter overridableParamater(){
return counter;
}
};
@Test(timeout = 1000)
public void testSingleInterruptsTriggering() throws Exception{
//Given
final InterruptCounter counter = new InterruptCounter();
TestInterruptHandlerFunction function = new TestInterruptHandlerFunction(counter);
//When
getInterruptable().requestInterrupts(function);
getInterruptable().enableInterrupts();
setInterruptLow();
Timer.delay(0.01);
//Note: Utility.getFPGATime() is used because double values can turn over after the robot has been running for a long time
final long interruptTriggerTime = Utility.getFPGATime();
setInterruptHigh();
//Delay until the interrupt is complete
while(!function.interruptComplete.get()){
Timer.delay(.005);
}
//Then
assertEquals("The interrupt did not fire the expected number of times", 1, counter.getCount());
//If the test within the interrupt failed
if(function.exceptionThrown.get()){
throw function.ex;
}
final long range = 10000; //in microseconds
assertThat("The interrupt did not fire within the expected time period (values in milliseconds)",
function.interruptFireTime.get(), both(greaterThan(interruptTriggerTime - range))
.and(lessThan(interruptTriggerTime + range)));
assertThat("The readInterruptTimestamp() did not return the correct value (values in seconds)",
getInterruptable().readInterruptTimestamp(), both(greaterThan((interruptTriggerTime - range)/1e6))
.and(lessThan((interruptTriggerTime + range)/1e6)));
}
@Test(timeout = 1000)
public void testMultipleInterruptsTriggering() throws Exception{
//Given
final InterruptCounter counter = new InterruptCounter();
TestInterruptHandlerFunction function = new TestInterruptHandlerFunction(counter);
//When
getInterruptable().requestInterrupts(function);
getInterruptable().enableInterrupts();
final int fireCount = 50;
for(int i = 0; i < fireCount; i ++){
setInterruptLow();
setInterruptHigh();
//Wait for the interrupt to complete before moving on
while(!function.interruptComplete.getAndSet(false)){
Timer.delay(.005);
}
}
//Then
assertEquals("The interrupt did not fire the expected number of times", fireCount, counter.getCount());
}
/** The timeout length for this test in seconds */
private static final int synchronousTimeout = 5;
@Test(timeout = (long)(synchronousTimeout*1e3))
public void testSynchronousInterruptsTriggering(){
//Given
getInterruptable().requestInterrupts();
final double synchronousDelay = synchronousTimeout/2.;
Runnable r = new Runnable(){
@Override
public void run() {
Timer.delay(synchronousDelay);
setInterruptLow();
setInterruptHigh();
}
};
//When
//Note: the long time value is used because doubles can flip if the robot is left running for long enough
final long startTimeStamp = Utility.getFPGATime();
new Thread(r).start();
//Delay for twice as long as the timeout so the test should fail first
getInterruptable().waitForInterrupt(synchronousTimeout * 2);
final long stopTimeStamp = Utility.getFPGATime();
//Then
//The test will not have timed out and:
final double interruptRunTime = (stopTimeStamp - startTimeStamp)*1e-6;
assertEquals("The interrupt did not run for the expected amount of time (units in seconds)", synchronousDelay, interruptRunTime, .1);
}
@Test(timeout = 2000)
public void testDisableStopsInterruptFiring(){
final InterruptCounter counter = new InterruptCounter();
TestInterruptHandlerFunction function = new TestInterruptHandlerFunction(counter);
//When
getInterruptable().requestInterrupts(function);
getInterruptable().enableInterrupts();
final int fireCount = 50;
for(int i = 0; i < fireCount; i ++){
setInterruptLow();
setInterruptHigh();
//Wait for the interrupt to complete before moving on
while(!function.interruptComplete.getAndSet(false)){
Timer.delay(.005);
}
}
getInterruptable().disableInterrupts();
//TestBench.out().println("Finished disabling the robot");
for(int i = 0; i < fireCount; i ++){
setInterruptLow();
setInterruptHigh();
//Just wait because the interrupt should not fire
Timer.delay(.005);
}
//Then
assertEquals("The interrupt did not fire the expected number of times", fireCount, counter.getCount());
}
}

View File

@@ -6,7 +6,9 @@
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.logging.Logger;
@@ -16,15 +18,15 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
/**
* @author jonathanleitschuh
*
*/
public class AnalogCrossConnectTest extends AbstractComsSetup {
public class AnalogCrossConnectTest extends AbstractInterruptTest {
private static final Logger logger = Logger.getLogger(AnalogCrossConnectTest.class.getName());
private static AnalogCrossConnectFixture analogIO;
@@ -133,7 +135,6 @@ public class AnalogCrossConnectTest extends AbstractComsSetup {
// Given
AnalogTrigger trigger = new AnalogTrigger(analogIO.getInput());
trigger.setLimitsVoltage(2.0f, 3.0f);
Counter counter = new Counter(trigger);
// When the analog output is turned low and high 50 times
@@ -152,4 +153,48 @@ public class AnalogCrossConnectTest extends AbstractComsSetup {
public void testRuntimeExceptionOnInvalidAccumulatorPort(){
analogIO.getInput().getAccumulatorCount();
}
private AnalogTrigger interruptTrigger;
private AnalogTriggerOutput interruptTriggerOutput;
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#giveInterruptableSensorBase()
*/
@Override
InterruptableSensorBase giveInterruptableSensorBase() {
interruptTrigger = new AnalogTrigger(analogIO.getInput());
interruptTrigger.setLimitsVoltage(2.0f, 3.0f);
interruptTriggerOutput = new AnalogTriggerOutput(interruptTrigger, AnalogTriggerType.STATE);
return interruptTriggerOutput;
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#freeInterruptableSensorBase()
*/
@Override
void freeInterruptableSensorBase() {
interruptTriggerOutput.cancelInterrupts();
interruptTriggerOutput.free();
interruptTriggerOutput = null;
interruptTrigger.free();
interruptTrigger = null;
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptHigh()
*/
@Override
void setInterruptHigh() {
analogIO.getOutput().setVoltage(4.0);
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptLow()
*/
@Override
void setInterruptLow() {
analogIO.getOutput().setVoltage(1.0);
}
}

View File

@@ -6,14 +6,12 @@
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import edu.wpi.first.wpilibj.fixtures.AnalogCrossConnectFixture;
@@ -68,8 +66,8 @@ public class AnalogPotentiometerTest extends AbstractComsSetup {
public void testRangeValues(){
for(double i = 0.0; i < 360.0; i = i+1.0){
potSource.setAngle(i);
assertEquals(i, pot.get(), DOUBLE_COMPARISON_DELTA);
Timer.delay(.02);
assertEquals(i, pot.get(), DOUBLE_COMPARISON_DELTA);
}
}

View File

@@ -14,16 +14,12 @@ import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import edu.wpi.first.wpilibj.fixtures.DIOCrossConnectFixture;
import edu.wpi.first.wpilibj.test.AbstractComsSetup;
import edu.wpi.first.wpilibj.test.TestBench;
/**
@@ -31,11 +27,12 @@ import edu.wpi.first.wpilibj.test.TestBench;
* @author jonathanleitschuh
*/
@RunWith(Parameterized.class)
public class DIOCrossConnectTest extends AbstractComsSetup {
public class DIOCrossConnectTest extends AbstractInterruptTest {
private static final Logger logger = Logger.getLogger(DIOCrossConnectTest.class.getName());
private static DIOCrossConnectFixture dio = null;
@Override
protected Logger getClassLogger(){
return logger;
}
@@ -65,7 +62,7 @@ public class DIOCrossConnectTest extends AbstractComsSetup {
* Array in the Collection, each array element corresponds to a parameter
* in the constructor.
*/
@Parameters
@Parameters(name= "{index}: Input Port: {0} Output Port: {1}")
public static Collection<Integer[]> generateData() {
// In this example, the parameter generator returns a List of
// arrays. Each array has two elements: { Digital input port, Digital output port}.
@@ -74,24 +71,14 @@ public class DIOCrossConnectTest extends AbstractComsSetup {
return TestBench.getInstance().getDIOCrossConnectCollection();
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
dio.teardown();
}
@Before
public void setUp() throws Exception {
dio.reset();
}
@After
public void tearDown() throws Exception {
dio.reset();
}
/**
@@ -115,4 +102,38 @@ public class DIOCrossConnectTest extends AbstractComsSetup {
Timer.delay(.02);
assertFalse("DIO Not Low after .05s delay", dio.getInput().get());
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#giveInterruptableSensorBase()
*/
@Override
InterruptableSensorBase giveInterruptableSensorBase() {
return dio.getInput();
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#freeInterruptableSensorBase()
*/
@Override
void freeInterruptableSensorBase() {
// Handled in the fixture
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptHigh()
*/
@Override
void setInterruptHigh() {
dio.getOutput().set(true);
}
/* (non-Javadoc)
* @see edu.wpi.first.wpilibj.AbstractInterruptTest#setInterruptLow()
*/
@Override
void setInterruptLow() {
dio.getOutput().set(false);
}
}

View File

@@ -52,6 +52,11 @@ public class DIOCrossConnectFixture implements ITestFixture {
@Override
public boolean reset() {
try{
input.cancelInterrupts();
} catch(IllegalStateException e) {
//This will happen if the interrupt has not been allocated for this test.
}
output.set(false);
return true;
}

View File

@@ -3,6 +3,8 @@
#include "Log.hpp"
#include "edu_wpi_first_wpilibj_hal_HALUtil.h"
#include "HAL/HAL.hpp"
#include "errno.h"
#include <string.h>
// set the logging level
TLogLevel halUtilLogLevel = logWARNING;
@@ -145,3 +147,27 @@ JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_hal_HALUtil_getHALErrorMess
HALUTIL_LOG(logDEBUG) << "Calling HALUtil getHALErrorMessage id=" << paramId << " msg=" << msg;
return paramEnv->NewStringUTF(msg);
}
/*
* Class: edu_wpi_first_wpilibj_hal_HALUtil
* Method: getHALErrno
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_HALUtil_getHALErrno
(JNIEnv *, jclass)
{
return errno;
}
/*
* Class: edu_wpi_first_wpilibj_hal_HALUtil
* Method: getHALstrerror
* Signature: (I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_edu_wpi_first_wpilibj_hal_HALUtil_getHALstrerror
(JNIEnv * env, jclass, jint errorCode)
{
const char * msg = strerror(errno);
HALUTIL_LOG(logDEBUG) << "Calling HALUtil getHALstrerror errorCode=" << errorCode << " msg=" << msg;
return env->NewStringUTF(msg);
}

View File

@@ -3,6 +3,33 @@
#include "Log.hpp"
#include "edu_wpi_first_wpilibj_hal_InterruptJNI.h"
#include "HAL/Interrupts.hpp"
TLogLevel interruptJNILogLevel = logERROR;
#define INTERRUPTJNI_LOG(level) \
if (level > interruptJNILogLevel) ; \
else Log().Get(level)
//Used for callback when an interrupt is fired.
static JavaVM *jvm;
/*
* Class: edu_wpi_first_wpilibj_hal_InterruptJNI
* Method: initializeInterruptJVM
* Signature: (Ljava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_initializeInterruptJVM
(JNIEnv * env, jclass, jobject status)
{
//This method should be called once to setup the JVM
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI initializeInterruptJVM";
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
jint rs = env->GetJavaVM(&jvm);
assert (rs == JNI_OK);
}
/*
* Class: edu_wpi_first_wpilibj_hal_InterruptJNI
@@ -10,22 +37,41 @@
* Signature: (IBLjava/nio/IntBuffer;)Ljava/nio/ByteBuffer;
*/
JNIEXPORT jobject JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_initializeInterrupts
(JNIEnv *, jclass, jint, jbyte, jobject)
(JNIEnv * env, jclass, jint interruptIndex, jbyte watcher, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI initializeInterrupts";
INTERRUPTJNI_LOG(logDEBUG) << "interruptIndex = " << interruptIndex;
INTERRUPTJNI_LOG(logDEBUG) << "watcher = " << (bool) watcher;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
void** interruptPtr = (void**)new unsigned char[4];
*interruptPtr = (void**) initializeInterrupts(interruptIndex, watcher, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *interruptPtr;
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
return env->NewDirectByteBuffer(interruptPtr, 4);
}
/*
* Class: edu_wpi_first_wpilibj_hal_InterruptJNI
* Method: cleanInterrupts
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_cleanInterrupts
(JNIEnv *, jclass, jobject, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI cleanInterrupts";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
cleanInterrupts(*javaId, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
/*
@@ -34,10 +80,17 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_cleanInterrup
* Signature: (Ljava/nio/ByteBuffer;DLjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_waitForInterrupt
(JNIEnv *, jclass, jobject, jdouble, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jdouble timeout, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI waitForInterrupt";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
waitForInterrupt(*javaId, timeout, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
/*
@@ -46,10 +99,17 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_waitForInterr
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_enableInterrupts
(JNIEnv *, jclass, jobject, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI enableInterrupts";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
enableInterrupts(*javaId, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
/*
@@ -58,10 +118,17 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_enableInterru
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_disableInterrupts
(JNIEnv *, jclass, jobject, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI disableInterrupts";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
disableInterrupts(*javaId, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
/*
@@ -70,10 +137,18 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_disableInterr
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)D
*/
JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_readInterruptTimestamp
(JNIEnv *, jclass, jobject, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI readInterruptTimestamp";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
jdouble timeStamp = readInterruptTimestamp(*javaId, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
return timeStamp;
}
/*
@@ -82,10 +157,73 @@ JNIEXPORT jdouble JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_readInterr
* Signature: (Ljava/nio/ByteBuffer;BIBLjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_requestInterrupts
(JNIEnv *, jclass, jobject, jbyte, jint, jbyte, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jbyte routing_module, jint routing_pin, jbyte routing_analog_trigger, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI requestInterrupts";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
INTERRUPTJNI_LOG(logDEBUG) << "routing module = " << (jint) routing_module;
INTERRUPTJNI_LOG(logDEBUG) << "routing pin = " << routing_pin;
INTERRUPTJNI_LOG(logDEBUG) << "routing analog trigger = " << (jint) routing_analog_trigger;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
requestInterrupts(*javaId, (uint8_t) routing_module, (uint32_t) routing_pin, routing_analog_trigger, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
struct InterruptHandlerParam {
/*
* The object edu/wpi/first/wpilibj/hal/InterruptJNI/InterruptHandlerFunction
* that contains the callback method [code]mid[/code].
*/
jobject handler_obj;
//The method id for the callback method
jmethodID mid;
//The params passed to the function
jobject param;
~InterruptHandlerParam(){
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI InterruptHandlerParam Destructor";
JNIEnv *env;
jint rs = jvm->AttachCurrentThread((void**)&env, NULL);
assert (rs == JNI_OK);
env->DeleteGlobalRef(handler_obj);
env->DeleteGlobalRef(param);
rs = jvm->DetachCurrentThread();
assert (rs == JNI_OK);
INTERRUPTJNI_LOG(logDEBUG) << "Leaving INTERRUPTJNI InterruptHandlerParam Destructor";
}
};
void interruptHandler(uint32_t mask, void *data) {
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI interruptHandler";
InterruptHandlerParam *param = static_cast<InterruptHandlerParam *>(data);
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam Ptr = " << param;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam->obj = " << param->handler_obj;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam->param = " << param->param;
//Because this is a callback in a new thread we must attach it to the JVM
JNIEnv *env;
jint rs = jvm->AttachCurrentThread((void**)&env, NULL);
assert (rs == JNI_OK);
INTERRUPTJNI_LOG(logDEBUG) << "Attached to thread";
env->CallVoidMethod(param->handler_obj, param->mid, mask, param->param);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
rs = jvm->DetachCurrentThread();
assert (rs == JNI_OK);
INTERRUPTJNI_LOG(logDEBUG) << "Leaving INTERRUPTJNI interruptHandler";
}
/*
@@ -94,10 +232,47 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_requestInterr
* Signature: (Ljava/nio/ByteBuffer;Ledu/wpi/first/wpilibj/hal/InterruptJNI/InterruptHandlerFunction;Ljava/nio/ByteBuffer;Ljava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_attachInterruptHandler
(JNIEnv *, jclass, jobject, jobject, jobject, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jobject handler, jobject param, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI attachInterruptHandler";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
//Store the interrupt callback paramaters
InterruptHandlerParam *interruptHandlerParam = new InterruptHandlerParam();
//Stores the object that contains the callback
interruptHandlerParam->handler_obj = env->NewGlobalRef(handler);
//The parameter that will be passed back to the JVM when the method is called
interruptHandlerParam->param = env->NewGlobalRef(param);
jclass cls = env->GetObjectClass(handler);
INTERRUPTJNI_LOG(logDEBUG) << "class = " << cls;
if (cls == 0) {
INTERRUPTJNI_LOG(logERROR) << "Error getting java class";
assert (false);
return;
}
jmethodID mid = env->GetMethodID(cls, "apply", "(ILjava/lang/Object;)V");
INTERRUPTJNI_LOG(logDEBUG) << "method = " << mid;
if (mid == 0) {
INTERRUPTJNI_LOG(logERROR) << "Error getting java method ID";
assert (false);
return;
}
interruptHandlerParam->mid = mid;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam Ptr = " << interruptHandlerParam;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam->obj (handler) = " << interruptHandlerParam->handler_obj;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam->mid = " << interruptHandlerParam->mid;
INTERRUPTJNI_LOG(logDEBUG) << "InterruptHandlerParam->param = " << interruptHandlerParam->param;
attachInterruptHandler(*javaId, interruptHandler, interruptHandlerParam, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}
/*
@@ -106,8 +281,18 @@ JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_attachInterru
* Signature: (Ljava/nio/ByteBuffer;BBLjava/nio/IntBuffer;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_first_wpilibj_hal_InterruptJNI_setInterruptUpSourceEdge
(JNIEnv *, jclass, jobject, jbyte, jbyte, jobject)
(JNIEnv * env, jclass, jobject interrupt_pointer, jbyte risingEdge, jbyte fallingEdge, jobject status)
{
assert(false);
INTERRUPTJNI_LOG(logDEBUG) << "Calling INTERRUPTJNI setInterruptUpSourceEdge";
void ** javaId = (void**)env->GetDirectBufferAddress(interrupt_pointer);
INTERRUPTJNI_LOG(logDEBUG) << "Interrupt Ptr = " << *javaId;
INTERRUPTJNI_LOG(logDEBUG) << "Rising Edge = " << (bool) risingEdge;
INTERRUPTJNI_LOG(logDEBUG) << "Falling Edge = " << (bool) fallingEdge;
jint * statusPtr = (jint*)env->GetDirectBufferAddress(status);
INTERRUPTJNI_LOG(logDEBUG) << "Status Ptr = " << statusPtr;
setInterruptUpSourceEdge(*javaId, risingEdge, fallingEdge, statusPtr);
INTERRUPTJNI_LOG(logDEBUG) << "Status = " << *statusPtr;
}