diff --git a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp b/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp index 220043f915..6805eb6615 100644 --- a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp +++ b/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. 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. */ @@ -14,6 +14,32 @@ using namespace frc; +InterruptableSensorBase::~InterruptableSensorBase() { + if (m_interrupt == HAL_kInvalidHandle) return; + int32_t status = 0; + auto param = HAL_CleanInterrupts(m_interrupt, &status); + if (param) { + delete reinterpret_cast(param); + } + // Ignore status, as an invalid handle just needs to be ignored. + m_interrupt = HAL_kInvalidHandle; +} + +InterruptableSensorBase::InterruptableSensorBase(InterruptableSensorBase&& rhs) + : ErrorBase(std::move(rhs)), + m_interrupt(rhs.m_interrupt.exchange(HAL_kInvalidHandle)) { + rhs.m_interrupt = HAL_kInvalidHandle; +} + +InterruptableSensorBase& InterruptableSensorBase::operator=( + InterruptableSensorBase&& rhs) { + ErrorBase::operator=(std::move(rhs)); + + m_interrupt = rhs.m_interrupt.exchange(HAL_kInvalidHandle); + + return *this; +} + void InterruptableSensorBase::RequestInterrupts( HAL_InterruptHandlerFunction handler, void* param) { if (StatusIsFatal()) return; @@ -32,6 +58,38 @@ void InterruptableSensorBase::RequestInterrupts( wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); } +void InterruptableSensorBase::RequestInterrupts(InterruptEventHandler handler) { + if (StatusIsFatal()) return; + + wpi_assert(m_interrupt == HAL_kInvalidHandle); + AllocateInterrupts(false); + if (StatusIsFatal()) return; // if allocate failed, out of interrupts + + auto handlerPtr = new InterruptEventHandler(std::move(handler)); + + int32_t status = 0; + HAL_RequestInterrupts( + m_interrupt, GetPortHandleForRouting(), + static_cast(GetAnalogTriggerTypeForRouting()), + &status); + SetUpSourceEdge(true, false); + HAL_AttachInterruptHandler( + m_interrupt, + [](uint32_t mask, void* param) { + auto self = reinterpret_cast(param); + // Rising edge result is the interrupt bit set in the byte 0xFF + // Falling edge result is the interrupt bit set in the byte 0xFF00 + // Set any bit set to be true for that edge, and AND the 2 results + // together to match the existing enum for all interrupts + int32_t rising = (mask & 0xFF) ? 0x1 : 0x0; + int32_t falling = ((mask & 0xFF00) ? 0x0100 : 0x0); + WaitResult res = static_cast(falling | rising); + (*self)(res); + }, + handlerPtr, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + void InterruptableSensorBase::RequestInterrupts() { if (StatusIsFatal()) return; @@ -52,7 +110,10 @@ void InterruptableSensorBase::CancelInterrupts() { if (StatusIsFatal()) return; wpi_assert(m_interrupt != HAL_kInvalidHandle); int32_t status = 0; - HAL_CleanInterrupts(m_interrupt, &status); + auto param = HAL_CleanInterrupts(m_interrupt, &status); + if (param) { + delete reinterpret_cast(param); + } // Ignore status, as an invalid handle just needs to be ignored. m_interrupt = HAL_kInvalidHandle; } diff --git a/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h b/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h index 7c9e3649f5..968587b223 100644 --- a/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h +++ b/wpilibc/src/main/native/include/frc/InterruptableSensorBase.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. 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. */ @@ -7,6 +7,9 @@ #pragma once +#include +#include + #include #include "frc/AnalogTriggerType.h" @@ -24,10 +27,22 @@ class InterruptableSensorBase : public ErrorBase, public SendableBase { kBoth = 0x101, }; + /** + * Handler for interrupts. + * + * First parameter is if rising, 2nd is if falling. + */ + using InterruptEventHandler = std::function; + InterruptableSensorBase() = default; - InterruptableSensorBase(InterruptableSensorBase&&) = default; - InterruptableSensorBase& operator=(InterruptableSensorBase&&) = default; + /** + * Free the resources for an interrupt event. + */ + virtual ~InterruptableSensorBase(); + + InterruptableSensorBase(InterruptableSensorBase&&); + InterruptableSensorBase& operator=(InterruptableSensorBase&&); virtual HAL_Handle GetPortHandleForRouting() const = 0; virtual AnalogTriggerType GetAnalogTriggerTypeForRouting() const = 0; @@ -43,6 +58,16 @@ class InterruptableSensorBase : public ErrorBase, public SendableBase { virtual void RequestInterrupts(HAL_InterruptHandlerFunction handler, void* param); + /** + * Request one of the 8 interrupts asynchronously on this digital input. + * + * Request interrupts in asynchronous mode where the user's interrupt handler + * will be called when the interrupt fires. Users that want control over the + * thread priority should use the synchronous method with their own spawned + * thread. The default is interrupt on rising edges only. + */ + virtual void RequestInterrupts(InterruptEventHandler handler); + /** * Request one of the 8 interrupts synchronously on this digital input. * @@ -119,7 +144,8 @@ class InterruptableSensorBase : public ErrorBase, public SendableBase { virtual void SetUpSourceEdge(bool risingEdge, bool fallingEdge); protected: - HAL_InterruptHandle m_interrupt = HAL_kInvalidHandle; + // atomic for proper destruction + std::atomic m_interrupt{HAL_kInvalidHandle}; void AllocateInterrupts(bool watcher); }; diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java index cc10fe1a86..d7d4025118 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/InterruptableSensorBase.java @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. 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. */ @@ -7,6 +7,8 @@ package edu.wpi.first.wpilibj; +import java.util.function.Consumer; + import edu.wpi.first.hal.InterruptJNI; import edu.wpi.first.hal.util.AllocationException; @@ -26,6 +28,18 @@ public abstract class InterruptableSensorBase extends SendableBase { WaitResult(int value) { this.value = value; } + + public static WaitResult getValue(boolean rising, boolean falling) { + if (rising && falling) { + return kBoth; + } else if (rising) { + return kRisingEdge; + } else if (falling) { + return kFallingEdge; + } else { + return kTimeout; + } + } } /** @@ -67,6 +81,36 @@ public abstract class InterruptableSensorBase extends SendableBase { */ public abstract int getPortHandleForRouting(); + /** + * Request one of the 8 interrupts asynchronously on this digital input. + * + * @param handler The {@link InterruptHandler} that contains the method {@link + * InterruptHandlerFunction#onInterrupt(boolean, boolean)} that will be called + * whenever there is an interrupt on this device. Request interrupts in synchronous + * 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(Consumer handler) { + if (m_interrupt != 0) { + throw new AllocationException("The interrupt has already been allocated"); + } + + allocateInterrupts(false); + + assert m_interrupt != 0; + + InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(), + getAnalogTriggerTypeForRouting()); + setUpSourceEdge(true, false); + InterruptJNI.attachInterruptHandler(m_interrupt, (mask, obj) -> { + // Rising edge result is the interrupt bit set in the byte 0xFF + // Falling edge result is the interrupt bit set in the byte 0xFF00 + boolean rising = (mask & 0xFF) != 0; + boolean falling = (mask & 0xFF00) != 0; + handler.accept(WaitResult.getValue(rising, falling)); + }, null); + } + /** * Request one of the 8 interrupts asynchronously on this digital input. * @@ -154,16 +198,9 @@ public abstract class InterruptableSensorBase extends SendableBase { // Falling edge result is the interrupt bit set in the byte 0xFF00 // Set any bit set to be true for that edge, and AND the 2 results // together to match the existing enum for all interrupts - int rising = ((result & 0xFF) != 0) ? 0x1 : 0x0; - int falling = ((result & 0xFF00) != 0) ? 0x0100 : 0x0; - result = rising | falling; - - for (WaitResult mode : WaitResult.values()) { - if (mode.value == result) { - return mode; - } - } - return null; + boolean rising = (result & 0xFF) != 0; + boolean falling = (result & 0xFF00) != 0; + return WaitResult.getValue(rising, falling); } /**