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}