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