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.CTREPCMJNI;
008import java.util.HashMap;
009import java.util.Map;
010
011/** Module class for controlling a Cross The Road Electronics Pneumatics Control Module. */
012public class PneumaticsControlModule implements PneumaticsBase {
013  private static class DataStore implements AutoCloseable {
014    public final int m_module;
015    public final int m_handle;
016    private int m_refCount;
017    private int m_reservedMask;
018    private boolean m_compressorReserved;
019    private final Object m_reserveLock = new Object();
020
021    DataStore(int module) {
022      m_handle = CTREPCMJNI.initialize(module);
023      m_module = module;
024      m_handleMap.put(module, this);
025    }
026
027    @Override
028    public void close() {
029      CTREPCMJNI.free(m_handle);
030      m_handleMap.remove(m_module);
031    }
032
033    public void addRef() {
034      m_refCount++;
035    }
036
037    public void removeRef() {
038      m_refCount--;
039      if (m_refCount == 0) {
040        this.close();
041      }
042    }
043  }
044
045  private static final Map<Integer, DataStore> m_handleMap = new HashMap<>();
046  private static final Object m_handleLock = new Object();
047
048  private static DataStore getForModule(int module) {
049    synchronized (m_handleLock) {
050      Integer moduleBoxed = module;
051      DataStore pcm = m_handleMap.get(moduleBoxed);
052      if (pcm == null) {
053        pcm = new DataStore(module);
054      }
055      pcm.addRef();
056      return pcm;
057    }
058  }
059
060  private static void freeModule(DataStore store) {
061    synchronized (m_handleLock) {
062      store.removeRef();
063    }
064  }
065
066  private final DataStore m_dataStore;
067  private final int m_handle;
068
069  /** Constructs a PneumaticsControlModule with the default ID (0). */
070  public PneumaticsControlModule() {
071    this(SensorUtil.getDefaultCTREPCMModule());
072  }
073
074  /**
075   * Constructs a PneumaticsControlModule.
076   *
077   * @param module module number to construct
078   */
079  public PneumaticsControlModule(int module) {
080    m_dataStore = getForModule(module);
081    m_handle = m_dataStore.m_handle;
082  }
083
084  @Override
085  public void close() {
086    freeModule(m_dataStore);
087  }
088
089  @Override
090  public boolean getCompressor() {
091    return CTREPCMJNI.getCompressor(m_handle);
092  }
093
094  @Override
095  public boolean getPressureSwitch() {
096    return CTREPCMJNI.getPressureSwitch(m_handle);
097  }
098
099  @Override
100  public double getCompressorCurrent() {
101    return CTREPCMJNI.getCompressorCurrent(m_handle);
102  }
103
104  /**
105   * Return whether the compressor current is currently too high.
106   *
107   * @return True if the compressor current is too high, otherwise false.
108   * @see #getCompressorCurrentTooHighStickyFault()
109   */
110  public boolean getCompressorCurrentTooHighFault() {
111    return CTREPCMJNI.getCompressorCurrentTooHighFault(m_handle);
112  }
113
114  /**
115   * Returns whether the compressor current has been too high since sticky faults were last cleared.
116   * This fault is persistent and can be cleared by {@link #clearAllStickyFaults()}
117   *
118   * @return True if the compressor current has been too high since sticky faults were last cleared.
119   * @see #getCompressorCurrentTooHighFault()
120   */
121  public boolean getCompressorCurrentTooHighStickyFault() {
122    return CTREPCMJNI.getCompressorCurrentTooHighStickyFault(m_handle);
123  }
124
125  /**
126   * Returns whether the compressor is currently shorted.
127   *
128   * @return True if the compressor is currently shorted, otherwise false.
129   * @see #getCompressorShortedStickyFault()
130   */
131  public boolean getCompressorShortedFault() {
132    return CTREPCMJNI.getCompressorShortedFault(m_handle);
133  }
134
135  /**
136   * Returns whether the compressor has been shorted since sticky faults were last cleared. This
137   * fault is persistent and can be cleared by {@link #clearAllStickyFaults()}
138   *
139   * @return True if the compressor has been shorted since sticky faults were last cleared,
140   *     otherwise false.
141   * @see #getCompressorShortedFault()
142   */
143  public boolean getCompressorShortedStickyFault() {
144    return CTREPCMJNI.getCompressorShortedStickyFault(m_handle);
145  }
146
147  /**
148   * Returns whether the compressor is currently disconnected.
149   *
150   * @return True if compressor is currently disconnected, otherwise false.
151   * @see #getCompressorNotConnectedStickyFault()
152   */
153  public boolean getCompressorNotConnectedFault() {
154    return CTREPCMJNI.getCompressorNotConnectedFault(m_handle);
155  }
156
157  /**
158   * Returns whether the compressor has been disconnected since sticky faults were last cleared.
159   * This fault is persistent and can be cleared by {@link #clearAllStickyFaults()}
160   *
161   * @return True if the compressor has been disconnected since sticky faults were last cleared,
162   *     otherwise false.
163   * @see #getCompressorNotConnectedFault()
164   */
165  public boolean getCompressorNotConnectedStickyFault() {
166    return CTREPCMJNI.getCompressorNotConnectedStickyFault(m_handle);
167  }
168
169  @Override
170  public void setSolenoids(int mask, int values) {
171    CTREPCMJNI.setSolenoids(m_handle, mask, values);
172  }
173
174  @Override
175  public int getSolenoids() {
176    return CTREPCMJNI.getSolenoids(m_handle);
177  }
178
179  @Override
180  public int getModuleNumber() {
181    return m_dataStore.m_module;
182  }
183
184  @Override
185  public int getSolenoidDisabledList() {
186    return CTREPCMJNI.getSolenoidDisabledList(m_handle);
187  }
188
189  public boolean getSolenoidVoltageFault() {
190    return CTREPCMJNI.getSolenoidVoltageFault(m_handle);
191  }
192
193  public boolean getSolenoidVoltageStickyFault() {
194    return CTREPCMJNI.getSolenoidVoltageStickyFault(m_handle);
195  }
196
197  /** Clears all sticky faults on this device. */
198  public void clearAllStickyFaults() {
199    CTREPCMJNI.clearAllStickyFaults(m_handle);
200  }
201
202  @Override
203  public void fireOneShot(int index) {
204    CTREPCMJNI.fireOneShot(m_handle, index);
205  }
206
207  @Override
208  public void setOneShotDuration(int index, int durMs) {
209    CTREPCMJNI.setOneShotDuration(m_handle, index, durMs);
210  }
211
212  @Override
213  public boolean checkSolenoidChannel(int channel) {
214    return CTREPCMJNI.checkSolenoidChannel(channel);
215  }
216
217  @Override
218  public int checkAndReserveSolenoids(int mask) {
219    synchronized (m_dataStore.m_reserveLock) {
220      if ((m_dataStore.m_reservedMask & mask) != 0) {
221        return m_dataStore.m_reservedMask & mask;
222      }
223      m_dataStore.m_reservedMask |= mask;
224      return 0;
225    }
226  }
227
228  @Override
229  public void unreserveSolenoids(int mask) {
230    synchronized (m_dataStore.m_reserveLock) {
231      m_dataStore.m_reservedMask &= ~mask;
232    }
233  }
234
235  @Override
236  public Solenoid makeSolenoid(int channel) {
237    return new Solenoid(m_dataStore.m_module, PneumaticsModuleType.CTREPCM, channel);
238  }
239
240  @Override
241  public DoubleSolenoid makeDoubleSolenoid(int forwardChannel, int reverseChannel) {
242    return new DoubleSolenoid(
243        m_dataStore.m_module, PneumaticsModuleType.CTREPCM, forwardChannel, reverseChannel);
244  }
245
246  @Override
247  public Compressor makeCompressor() {
248    return new Compressor(m_dataStore.m_module, PneumaticsModuleType.CTREPCM);
249  }
250
251  @Override
252  public boolean reserveCompressor() {
253    synchronized (m_dataStore.m_reserveLock) {
254      if (m_dataStore.m_compressorReserved) {
255        return false;
256      }
257      m_dataStore.m_compressorReserved = true;
258      return true;
259    }
260  }
261
262  @Override
263  public void unreserveCompressor() {
264    synchronized (m_dataStore.m_reserveLock) {
265      m_dataStore.m_compressorReserved = false;
266    }
267  }
268
269  /**
270   * Disables the compressor. The compressor will not turn on until {@link
271   * #enableCompressorDigital()} is called.
272   */
273  @Override
274  public void disableCompressor() {
275    CTREPCMJNI.setClosedLoopControl(m_handle, false);
276  }
277
278  @Override
279  public void enableCompressorDigital() {
280    CTREPCMJNI.setClosedLoopControl(m_handle, true);
281  }
282
283  /**
284   * Enables the compressor in digital mode. Analog mode is unsupported by the CTRE PCM.
285   *
286   * @param minPressure Unsupported.
287   * @param maxPressure Unsupported.
288   * @see #enableCompressorDigital()
289   */
290  @Override
291  public void enableCompressorAnalog(double minPressure, double maxPressure) {
292    CTREPCMJNI.setClosedLoopControl(m_handle, false);
293  }
294
295  /**
296   * Enables the compressor in digital mode. Hybrid mode is unsupported by the CTRE PCM.
297   *
298   * @param minPressure Unsupported.
299   * @param maxPressure Unsupported.
300   * @see #enableCompressorDigital()
301   */
302  @Override
303  public void enableCompressorHybrid(double minPressure, double maxPressure) {
304    CTREPCMJNI.setClosedLoopControl(m_handle, false);
305  }
306
307  @Override
308  public CompressorConfigType getCompressorConfigType() {
309    return CTREPCMJNI.getClosedLoopControl(m_handle)
310        ? CompressorConfigType.Digital
311        : CompressorConfigType.Disabled;
312  }
313
314  /**
315   * Unsupported by the CTRE PCM.
316   *
317   * @param channel Unsupported.
318   * @return 0
319   */
320  @Override
321  public double getAnalogVoltage(int channel) {
322    return 0;
323  }
324
325  /**
326   * Unsupported by the CTRE PCM.
327   *
328   * @param channel Unsupported.
329   * @return 0
330   */
331  @Override
332  public double getPressure(int channel) {
333    return 0;
334  }
335}