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