001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import edu.wpi.first.hal.AnalogJNI;
008import edu.wpi.first.hal.FRCNetComm.tResourceType;
009import edu.wpi.first.hal.HAL;
010import edu.wpi.first.hal.util.BoundaryException;
011import edu.wpi.first.util.sendable.Sendable;
012import edu.wpi.first.util.sendable.SendableBuilder;
013import edu.wpi.first.util.sendable.SendableRegistry;
014import edu.wpi.first.wpilibj.AnalogTriggerOutput.AnalogTriggerType;
015
016/** Class for creating and configuring Analog Triggers. */
017public class AnalogTrigger implements Sendable, AutoCloseable {
018  /** Exceptions dealing with improper operation of the Analog trigger. */
019  @SuppressWarnings("serial")
020  public static class AnalogTriggerException extends RuntimeException {
021    /**
022     * Create a new exception with the given message.
023     *
024     * @param message the message to pass with the exception
025     */
026    public AnalogTriggerException(String message) {
027      super(message);
028    }
029  }
030
031  /** Where the analog trigger is attached. */
032  protected int m_port;
033
034  protected AnalogInput m_analogInput;
035  protected DutyCycle m_dutyCycle;
036  protected boolean m_ownsAnalog;
037
038  /**
039   * Constructor for an analog trigger given a channel number.
040   *
041   * @param channel the port to use for the analog trigger
042   */
043  public AnalogTrigger(final int channel) {
044    this(new AnalogInput(channel));
045    m_ownsAnalog = true;
046    SendableRegistry.addChild(this, m_analogInput);
047  }
048
049  /**
050   * Construct an analog trigger given an analog channel. This should be used in the case of sharing
051   * an analog channel between the trigger and an analog input object.
052   *
053   * @param channel the AnalogInput to use for the analog trigger
054   */
055  public AnalogTrigger(AnalogInput channel) {
056    m_analogInput = channel;
057
058    m_port = AnalogJNI.initializeAnalogTrigger(channel.m_port);
059
060    int index = getIndex();
061
062    HAL.report(tResourceType.kResourceType_AnalogTrigger, index + 1);
063    SendableRegistry.addLW(this, "AnalogTrigger", index);
064  }
065
066  /**
067   * Construct an analog trigger given a duty cycle input.
068   *
069   * @param input the DutyCycle to use for the analog trigger
070   */
071  public AnalogTrigger(DutyCycle input) {
072    m_dutyCycle = input;
073
074    m_port = AnalogJNI.initializeAnalogTriggerDutyCycle(input.m_handle);
075
076    int index = getIndex();
077
078    HAL.report(tResourceType.kResourceType_AnalogTrigger, index + 1);
079    SendableRegistry.addLW(this, "AnalogTrigger", index);
080  }
081
082  @Override
083  public void close() {
084    SendableRegistry.remove(this);
085    AnalogJNI.cleanAnalogTrigger(m_port);
086    m_port = 0;
087    if (m_ownsAnalog && m_analogInput != null) {
088      m_analogInput.close();
089    }
090  }
091
092  /**
093   * Set the upper and lower limits of the analog trigger. The limits are given in ADC codes. If
094   * oversampling is used, the units must be scaled appropriately.
095   *
096   * @param lower the lower raw limit
097   * @param upper the upper raw limit
098   */
099  public void setLimitsRaw(final int lower, final int upper) {
100    if (lower > upper) {
101      throw new BoundaryException("Lower bound is greater than upper");
102    }
103    AnalogJNI.setAnalogTriggerLimitsRaw(m_port, lower, upper);
104  }
105
106  /**
107   * Set the upper and lower limits of the analog trigger. The limits are given as floating point
108   * values between 0 and 1.
109   *
110   * @param lower the lower duty cycle limit
111   * @param upper the upper duty cycle limit
112   */
113  public void setLimitsDutyCycle(double lower, double upper) {
114    if (lower > upper) {
115      throw new BoundaryException("Lower bound is greater than upper bound");
116    }
117    AnalogJNI.setAnalogTriggerLimitsDutyCycle(m_port, lower, upper);
118  }
119
120  /**
121   * Set the upper and lower limits of the analog trigger. The limits are given as floating point
122   * voltage values.
123   *
124   * @param lower the lower voltage limit
125   * @param upper the upper voltage limit
126   */
127  public void setLimitsVoltage(double lower, double upper) {
128    if (lower > upper) {
129      throw new BoundaryException("Lower bound is greater than upper bound");
130    }
131    AnalogJNI.setAnalogTriggerLimitsVoltage(m_port, lower, upper);
132  }
133
134  /**
135   * Configure the analog trigger to use the averaged vs. raw values. If the value is true, then the
136   * averaged value is selected for the analog trigger, otherwise the immediate value is used.
137   *
138   * @param useAveragedValue true to use an averaged value, false otherwise
139   */
140  public void setAveraged(boolean useAveragedValue) {
141    AnalogJNI.setAnalogTriggerAveraged(m_port, useAveragedValue);
142  }
143
144  /**
145   * Configure the analog trigger to use a filtered value. The analog trigger will operate with a 3
146   * point average rejection filter. This is designed to help with 360 degree pot applications for
147   * the period where the pot crosses through zero.
148   *
149   * @param useFilteredValue true to use a filtered value, false otherwise
150   */
151  public void setFiltered(boolean useFilteredValue) {
152    AnalogJNI.setAnalogTriggerFiltered(m_port, useFilteredValue);
153  }
154
155  /**
156   * Return the index of the analog trigger. This is the FPGA index of this analog trigger instance.
157   *
158   * @return The index of the analog trigger.
159   */
160  public final int getIndex() {
161    return AnalogJNI.getAnalogTriggerFPGAIndex(m_port);
162  }
163
164  /**
165   * Return the InWindow output of the analog trigger. True if the analog input is between the upper
166   * and lower limits.
167   *
168   * @return The InWindow output of the analog trigger.
169   */
170  public boolean getInWindow() {
171    return AnalogJNI.getAnalogTriggerInWindow(m_port);
172  }
173
174  /**
175   * Return the TriggerState output of the analog trigger. True if above upper limit. False if below
176   * lower limit. If in Hysteresis, maintain previous state.
177   *
178   * @return The TriggerState output of the analog trigger.
179   */
180  public boolean getTriggerState() {
181    return AnalogJNI.getAnalogTriggerTriggerState(m_port);
182  }
183
184  /**
185   * Creates an AnalogTriggerOutput object. Gets an output object that can be used for routing.
186   * Caller is responsible for deleting the AnalogTriggerOutput object.
187   *
188   * @param type An enum of the type of output object to create.
189   * @return A pointer to a new AnalogTriggerOutput object.
190   */
191  public AnalogTriggerOutput createOutput(AnalogTriggerType type) {
192    return new AnalogTriggerOutput(this, type);
193  }
194
195  @Override
196  public void initSendable(SendableBuilder builder) {
197    if (m_ownsAnalog) {
198      m_analogInput.initSendable(builder);
199    }
200  }
201}