001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType; 011import edu.wpi.first.wpilibj.hal.HAL; 012import edu.wpi.first.wpilibj.hal.SolenoidJNI; 013import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; 014 015/** 016 * DoubleSolenoid class for running 2 channels of high voltage Digital Output on the PCM. 017 * 018 * <p>The DoubleSolenoid class is typically used for pneumatics solenoids that have two positions 019 * controlled by two separate channels. 020 */ 021public class DoubleSolenoid extends SolenoidBase implements Sendable { 022 /** 023 * Possible values for a DoubleSolenoid. 024 */ 025 public enum Value { 026 kOff, 027 kForward, 028 kReverse 029 } 030 031 private byte m_forwardMask; // The mask for the forward channel. 032 private byte m_reverseMask; // The mask for the reverse channel. 033 private int m_forwardHandle = 0; 034 private int m_reverseHandle = 0; 035 036 /** 037 * Constructor. Uses the default PCM ID (defaults to 0). 038 * 039 * @param forwardChannel The forward channel number on the PCM (0..7). 040 * @param reverseChannel The reverse channel number on the PCM (0..7). 041 */ 042 public DoubleSolenoid(final int forwardChannel, final int reverseChannel) { 043 this(SensorBase.getDefaultSolenoidModule(), forwardChannel, reverseChannel); 044 } 045 046 /** 047 * Constructor. 048 * 049 * @param moduleNumber The module number of the solenoid module to use. 050 * @param forwardChannel The forward channel on the module to control (0..7). 051 * @param reverseChannel The reverse channel on the module to control (0..7). 052 */ 053 public DoubleSolenoid(final int moduleNumber, final int forwardChannel, 054 final int reverseChannel) { 055 super(moduleNumber); 056 057 SensorBase.checkSolenoidModule(m_moduleNumber); 058 SensorBase.checkSolenoidChannel(forwardChannel); 059 SensorBase.checkSolenoidChannel(reverseChannel); 060 061 int portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) forwardChannel); 062 m_forwardHandle = SolenoidJNI.initializeSolenoidPort(portHandle); 063 064 try { 065 portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) reverseChannel); 066 m_reverseHandle = SolenoidJNI.initializeSolenoidPort(portHandle); 067 } catch (RuntimeException ex) { 068 // free the forward handle on exception, then rethrow 069 SolenoidJNI.freeSolenoidPort(m_forwardHandle); 070 m_forwardHandle = 0; 071 m_reverseHandle = 0; 072 throw ex; 073 } 074 075 m_forwardMask = (byte) (1 << forwardChannel); 076 m_reverseMask = (byte) (1 << reverseChannel); 077 078 HAL.report(tResourceType.kResourceType_Solenoid, forwardChannel, 079 m_moduleNumber); 080 HAL.report(tResourceType.kResourceType_Solenoid, reverseChannel, 081 m_moduleNumber); 082 setName("DoubleSolenoid", m_moduleNumber, forwardChannel); 083 } 084 085 /** 086 * Destructor. 087 */ 088 @Override 089 public synchronized void free() { 090 super.free(); 091 SolenoidJNI.freeSolenoidPort(m_forwardHandle); 092 SolenoidJNI.freeSolenoidPort(m_reverseHandle); 093 super.free(); 094 } 095 096 /** 097 * Set the value of a solenoid. 098 * 099 * @param value The value to set (Off, Forward, Reverse) 100 */ 101 public void set(final Value value) { 102 boolean forward = false; 103 boolean reverse = false; 104 105 switch (value) { 106 case kOff: 107 forward = false; 108 reverse = false; 109 break; 110 case kForward: 111 forward = true; 112 reverse = false; 113 break; 114 case kReverse: 115 forward = false; 116 reverse = true; 117 break; 118 default: 119 throw new AssertionError("Illegal value: " + value); 120 121 } 122 123 SolenoidJNI.setSolenoid(m_forwardHandle, forward); 124 SolenoidJNI.setSolenoid(m_reverseHandle, reverse); 125 } 126 127 /** 128 * Read the current value of the solenoid. 129 * 130 * @return The current value of the solenoid. 131 */ 132 public Value get() { 133 boolean valueForward = SolenoidJNI.getSolenoid(m_forwardHandle); 134 boolean valueReverse = SolenoidJNI.getSolenoid(m_reverseHandle); 135 136 if (valueForward) { 137 return Value.kForward; 138 } 139 if (valueReverse) { 140 return Value.kReverse; 141 } 142 return Value.kOff; 143 } 144 145 /** 146 * Check if the forward solenoid is blacklisted. If a solenoid is shorted, it is added to the 147 * blacklist and disabled until power cycle, or until faults are cleared. 148 * 149 * @return If solenoid is disabled due to short. 150 * @see #clearAllPCMStickyFaults() 151 */ 152 public boolean isFwdSolenoidBlackListed() { 153 int blackList = getPCMSolenoidBlackList(); 154 return (blackList & m_forwardMask) != 0; 155 } 156 157 /** 158 * Check if the reverse solenoid is blacklisted. If a solenoid is shorted, it is added to the 159 * blacklist and disabled until power cycle, or until faults are cleared. 160 * 161 * @return If solenoid is disabled due to short. 162 * @see #clearAllPCMStickyFaults() 163 */ 164 public boolean isRevSolenoidBlackListed() { 165 int blackList = getPCMSolenoidBlackList(); 166 return (blackList & m_reverseMask) != 0; 167 } 168 169 @Override 170 public void initSendable(SendableBuilder builder) { 171 builder.setSmartDashboardType("Double Solenoid"); 172 builder.setSafeState(() -> set(Value.kOff)); 173 builder.addStringProperty("Value", () -> get().name().substring(1), (value) -> { 174 if ("Forward".equals(value)) { 175 set(Value.kForward); 176 } else if ("Reverse".equals(value)) { 177 set(Value.kReverse); 178 } else { 179 set(Value.kOff); 180 } 181 }); 182 } 183}