diff --git a/hal/include/HAL/Interrupts.hpp b/hal/include/HAL/Interrupts.hpp index 4406cc1565..f5614f5843 100644 --- a/hal/include/HAL/Interrupts.hpp +++ b/hal/include/HAL/Interrupts.hpp @@ -6,6 +6,9 @@ #include #endif +#include +#include "errno.h" + extern "C" { typedef void (*InterruptHandlerFunction)(uint32_t interruptAssertedMask, void *param); diff --git a/hal/lib/Athena/Interrupts.cpp b/hal/lib/Athena/Interrupts.cpp index 941118d4d6..aad7b08647 100644 --- a/hal/lib/Athena/Interrupts.cpp +++ b/hal/lib/Athena/Interrupts.cpp @@ -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); + } /** diff --git a/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0 b/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0 index 6b511629e9..1891aba6c5 100755 Binary files a/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0 and b/ni-libraries/libRoboRIO_FRC_ChipObject.so.1.2.0 differ diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java index 6c6dcaea8f..b79c1820f5 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/AnalogTriggerOutput.java @@ -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 diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java index 1a2d8ebd7f..f1f610e285 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalInput.java @@ -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() { } } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java index 7365648da9..0d58088162 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalOutput.java @@ -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); diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java index 9fda2678fa..11b2760ed2 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/DigitalSource.java @@ -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; } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptHandlerFunction.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptHandlerFunction.java new file mode 100644 index 0000000000..747c1e8f5f --- /dev/null +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptHandlerFunction.java @@ -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 The type of the parameter that should be returned to the the + * method {@link #interruptFired(int, Object)} + */ +public abstract class InterruptHandlerFunction{ + /** + * 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; + } +} \ No newline at end of file diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java index f38b2c06b5..d92c28afb0 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java @@ -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"); + } + } } diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java index 7cb5bcd464..86d5b08f5e 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/HALUtil.java @@ -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) { diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/InterruptJNI.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/InterruptJNI.java index 23b722c66a..9e18d7a18d 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/InterruptJNI.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/hal/InterruptJNI.java @@ -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); } diff --git a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AbstractInterruptTest.java b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AbstractInterruptTest.java new file mode 100644 index 0000000000..967bfefddb --- /dev/null +++ b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AbstractInterruptTest.java @@ -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{ + 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()); + } + +} diff --git a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogCrossConnectTest.java b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogCrossConnectTest.java index 05f77d99be..dc99d03024 100644 --- a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogCrossConnectTest.java +++ b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogCrossConnectTest.java @@ -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); + } } diff --git a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometerTest.java b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometerTest.java index ab8ae65b3d..0c6de20a3e 100644 --- a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometerTest.java +++ b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/AnalogPotentiometerTest.java @@ -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); } } diff --git a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DIOCrossConnectTest.java b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DIOCrossConnectTest.java index ac15b97786..3ee5dd475e 100644 --- a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DIOCrossConnectTest.java +++ b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/DIOCrossConnectTest.java @@ -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 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); + + } + } diff --git a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/DIOCrossConnectFixture.java b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/DIOCrossConnectFixture.java index 5088d2349c..9abd6733c0 100644 --- a/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/DIOCrossConnectFixture.java +++ b/wpilibj/wpilibJavaIntegrationTests/src/main/java/edu/wpi/first/wpilibj/fixtures/DIOCrossConnectFixture.java @@ -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; } diff --git a/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp b/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp index e1491a3709..1d0bb39a4b 100644 --- a/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp +++ b/wpilibj/wpilibJavaJNI/lib/HALUtil.cpp @@ -3,6 +3,8 @@ #include "Log.hpp" #include "edu_wpi_first_wpilibj_hal_HALUtil.h" #include "HAL/HAL.hpp" +#include "errno.h" +#include // 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); +} diff --git a/wpilibj/wpilibJavaJNI/lib/InterruptJNI.cpp b/wpilibj/wpilibJavaJNI/lib/InterruptJNI.cpp index 9aeea3d243..6173c8f47c 100644 --- a/wpilibj/wpilibJavaJNI/lib/InterruptJNI.cpp +++ b/wpilibj/wpilibJavaJNI/lib/InterruptJNI.cpp @@ -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(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; }