Merge "Adds interrupts to Java"

This commit is contained in:
Brad Miller (WPI)
2014-08-20 13:39:47 -07:00
committed by Gerrit Code Review
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;
}