001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.InterruptJNI;
011import edu.wpi.first.wpilibj.util.AllocationException;
012
013
014/**
015 * Base for sensors to be used with interrupts.
016 */
017public abstract class InterruptableSensorBase extends SensorBase {
018
019  @SuppressWarnings("JavadocMethod")
020  public enum WaitResult {
021    kTimeout(0x0), kRisingEdge(0x1), kFallingEdge(0x100), kBoth(0x101);
022
023    @SuppressWarnings("MemberName")
024    public final int value;
025
026    private WaitResult(int value) {
027      this.value = value;
028    }
029  }
030
031  /**
032   * The interrupt resource.
033   */
034  protected int m_interrupt = InterruptJNI.HalInvalidHandle;
035
036  /**
037   * Flags if the interrupt being allocated is synchronous.
038   */
039  protected boolean m_isSynchronousInterrupt = false;
040
041  /**
042   * Create a new InterrupatableSensorBase.
043   */
044  public InterruptableSensorBase() {
045    m_interrupt = 0;
046  }
047
048  /**
049   * If this is an analog trigger.
050   *
051   * @return true if this is an analog trigger.
052   */
053  public abstract int getAnalogTriggerTypeForRouting();
054
055  /**
056   * The channel routing number.
057   *
058   * @return channel routing number
059   */
060  public abstract int getPortHandleForRouting();
061
062  /**
063   * Request one of the 8 interrupts asynchronously on this digital input.
064   *
065   * @param handler The {@link InterruptHandlerFunction} that contains the method {@link
066   *                InterruptHandlerFunction#interruptFired(int, Object)} that will be called
067   *                whenever there is an interrupt on this device. Request interrupts in synchronous
068   *                mode where the user program interrupt handler will be called when an interrupt
069   *                occurs. The default is interrupt on rising edges only.
070   */
071  public void requestInterrupts(InterruptHandlerFunction<?> handler) {
072    if (m_interrupt != 0) {
073      throw new AllocationException("The interrupt has already been allocated");
074    }
075
076    allocateInterrupts(false);
077
078    assert m_interrupt != 0;
079
080    InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(),
081                                   getAnalogTriggerTypeForRouting());
082    setUpSourceEdge(true, false);
083    InterruptJNI.attachInterruptHandler(m_interrupt, handler.m_function,
084        handler.overridableParameter());
085  }
086
087  /**
088   * Request one of the 8 interrupts synchronously on this digital input. Request interrupts in
089   * synchronous mode where the user program will have to explicitly wait for the interrupt to occur
090   * using {@link #waitForInterrupt}. The default is interrupt on rising edges only.
091   */
092  public void requestInterrupts() {
093    if (m_interrupt != 0) {
094      throw new AllocationException("The interrupt has already been allocated");
095    }
096
097    allocateInterrupts(true);
098
099    assert m_interrupt != 0;
100
101    InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(),
102                                   getAnalogTriggerTypeForRouting());
103    setUpSourceEdge(true, false);
104
105  }
106
107  /**
108   * Allocate the interrupt
109   *
110   * @param watcher true if the interrupt should be in synchronous mode where the user program will
111   *                have to explicitly wait for the interrupt to occur.
112   */
113  protected void allocateInterrupts(boolean watcher) {
114    m_isSynchronousInterrupt = watcher;
115
116    m_interrupt = InterruptJNI.initializeInterrupts(watcher);
117  }
118
119  /**
120   * Cancel interrupts on this device. This deallocates all the chipobject structures and disables
121   * any interrupts.
122   */
123  public void cancelInterrupts() {
124    if (m_interrupt == 0) {
125      throw new IllegalStateException("The interrupt is not allocated.");
126    }
127    InterruptJNI.cleanInterrupts(m_interrupt);
128    m_interrupt = 0;
129  }
130
131  /**
132   * In synchronous mode, wait for the defined interrupt to occur.
133   *
134   * @param timeout        Timeout in seconds
135   * @param ignorePrevious If true, ignore interrupts that happened before waitForInterrupt was
136   *                       called.
137   * @return Result of the wait.
138   */
139  public WaitResult waitForInterrupt(double timeout, boolean ignorePrevious) {
140    if (m_interrupt == 0) {
141      throw new IllegalStateException("The interrupt is not allocated.");
142    }
143    int result = InterruptJNI.waitForInterrupt(m_interrupt, timeout, ignorePrevious);
144
145    for (WaitResult mode : WaitResult.values()) {
146      if (mode.value == result) {
147        return mode;
148      }
149    }
150    return null;
151  }
152
153  /**
154   * In synchronous mode, wait for the defined interrupt to occur.
155   *
156   * @param timeout Timeout in seconds
157   * @return Result of the wait.
158   */
159  public WaitResult waitForInterrupt(double timeout) {
160    return waitForInterrupt(timeout, true);
161  }
162
163  /**
164   * Enable interrupts to occur on this input. Interrupts are disabled when the RequestInterrupt
165   * call is made. This gives time to do the setup of the other options before starting to field
166   * interrupts.
167   */
168  public void enableInterrupts() {
169    if (m_interrupt == 0) {
170      throw new IllegalStateException("The interrupt is not allocated.");
171    }
172    if (m_isSynchronousInterrupt) {
173      throw new IllegalStateException("You do not need to enable synchronous interrupts");
174    }
175    InterruptJNI.enableInterrupts(m_interrupt);
176  }
177
178  /**
179   * Disable Interrupts without without deallocating structures.
180   */
181  public void disableInterrupts() {
182    if (m_interrupt == 0) {
183      throw new IllegalStateException("The interrupt is not allocated.");
184    }
185    if (m_isSynchronousInterrupt) {
186      throw new IllegalStateException("You can not disable synchronous interrupts");
187    }
188    InterruptJNI.disableInterrupts(m_interrupt);
189  }
190
191  /**
192   * Return the timestamp for the rising interrupt that occurred most recently. This is in the same
193   * time domain as getClock(). The rising-edge interrupt should be enabled with {@link
194   * #setUpSourceEdge}.
195   *
196   * @return Timestamp in seconds since boot.
197   */
198  public double readRisingTimestamp() {
199    if (m_interrupt == 0) {
200      throw new IllegalStateException("The interrupt is not allocated.");
201    }
202    return InterruptJNI.readInterruptRisingTimestamp(m_interrupt);
203  }
204
205  /**
206   * Return the timestamp for the falling interrupt that occurred most recently. This is in the same
207   * time domain as getClock(). The falling-edge interrupt should be enabled with {@link
208   * #setUpSourceEdge}.
209   *
210   * @return Timestamp in seconds since boot.
211   */
212  public double readFallingTimestamp() {
213    if (m_interrupt == 0) {
214      throw new IllegalStateException("The interrupt is not allocated.");
215    }
216    return InterruptJNI.readInterruptFallingTimestamp(m_interrupt);
217  }
218
219  /**
220   * Set which edge to trigger interrupts on.
221   *
222   * @param risingEdge  true to interrupt on rising edge
223   * @param fallingEdge true to interrupt on falling edge
224   */
225  public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
226    if (m_interrupt != 0) {
227      InterruptJNI.setInterruptUpSourceEdge(m_interrupt, risingEdge,
228          fallingEdge);
229    } else {
230      throw new IllegalArgumentException("You must call RequestInterrupts before setUpSourceEdge");
231    }
232  }
233}