001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2017. 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.livewindow.LiveWindow; 014import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 015import edu.wpi.first.wpilibj.tables.ITable; 016import edu.wpi.first.wpilibj.tables.ITableListener; 017 018/** 019 * DoubleSolenoid class for running 2 channels of high voltage Digital Output on the PCM. 020 * 021 * <p>The DoubleSolenoid class is typically used for pneumatics solenoids that have two positions 022 * controlled by two separate channels. 023 */ 024public class DoubleSolenoid extends SolenoidBase implements LiveWindowSendable { 025 026 /** 027 * Possible values for a DoubleSolenoid. 028 */ 029 public enum Value { 030 kOff, 031 kForward, 032 kReverse 033 } 034 035 private byte m_forwardMask; // The mask for the forward channel. 036 private byte m_reverseMask; // The mask for the reverse channel. 037 private int m_forwardHandle = 0; 038 private int m_reverseHandle = 0; 039 040 /** 041 * Constructor. Uses the default PCM ID (defaults to 0). 042 * 043 * @param forwardChannel The forward channel number on the PCM (0..7). 044 * @param reverseChannel The reverse channel number on the PCM (0..7). 045 */ 046 public DoubleSolenoid(final int forwardChannel, final int reverseChannel) { 047 this(getDefaultSolenoidModule(), forwardChannel, reverseChannel); 048 } 049 050 /** 051 * Constructor. 052 * 053 * @param moduleNumber The module number of the solenoid module to use. 054 * @param forwardChannel The forward channel on the module to control (0..7). 055 * @param reverseChannel The reverse channel on the module to control (0..7). 056 */ 057 public DoubleSolenoid(final int moduleNumber, final int forwardChannel, 058 final int reverseChannel) { 059 super(moduleNumber); 060 061 checkSolenoidModule(m_moduleNumber); 062 checkSolenoidChannel(forwardChannel); 063 checkSolenoidChannel(reverseChannel); 064 065 int portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) forwardChannel); 066 m_forwardHandle = SolenoidJNI.initializeSolenoidPort(portHandle); 067 068 try { 069 portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) reverseChannel); 070 m_reverseHandle = SolenoidJNI.initializeSolenoidPort(portHandle); 071 } catch (RuntimeException ex) { 072 // free the forward handle on exception, then rethrow 073 SolenoidJNI.freeSolenoidPort(m_forwardHandle); 074 m_forwardHandle = 0; 075 m_reverseHandle = 0; 076 throw ex; 077 } 078 079 m_forwardMask = (byte) (1 << forwardChannel); 080 m_reverseMask = (byte) (1 << reverseChannel); 081 082 HAL.report(tResourceType.kResourceType_Solenoid, forwardChannel, 083 m_moduleNumber); 084 HAL.report(tResourceType.kResourceType_Solenoid, reverseChannel, 085 m_moduleNumber); 086 LiveWindow.addActuator("DoubleSolenoid", m_moduleNumber, forwardChannel, this); 087 } 088 089 /** 090 * Destructor. 091 */ 092 public synchronized void free() { 093 SolenoidJNI.freeSolenoidPort(m_forwardHandle); 094 SolenoidJNI.freeSolenoidPort(m_reverseHandle); 095 super.free(); 096 } 097 098 /** 099 * Set the value of a solenoid. 100 * 101 * @param value The value to set (Off, Forward, Reverse) 102 */ 103 public void set(final Value value) { 104 boolean forward = false; 105 boolean reverse = false; 106 107 switch (value) { 108 case kOff: 109 forward = false; 110 reverse = false; 111 break; 112 case kForward: 113 forward = true; 114 reverse = false; 115 break; 116 case kReverse: 117 forward = false; 118 reverse = true; 119 break; 120 default: 121 throw new AssertionError("Illegal value: " + value); 122 123 } 124 125 SolenoidJNI.setSolenoid(m_forwardHandle, forward); 126 SolenoidJNI.setSolenoid(m_reverseHandle, reverse); 127 } 128 129 /** 130 * Read the current value of the solenoid. 131 * 132 * @return The current value of the solenoid. 133 */ 134 public Value get() { 135 boolean valueForward = SolenoidJNI.getSolenoid(m_forwardHandle); 136 boolean valueReverse = SolenoidJNI.getSolenoid(m_reverseHandle); 137 138 if (valueForward) { 139 return Value.kForward; 140 } 141 if (valueReverse) { 142 return Value.kReverse; 143 } 144 return Value.kOff; 145 } 146 147 /** 148 * Check if the forward solenoid is blacklisted. If a solenoid is shorted, it is added to the 149 * blacklist and disabled until power cycle, or until faults are cleared. 150 * 151 * @return If solenoid is disabled due to short. 152 * @see #clearAllPCMStickyFaults() 153 */ 154 public boolean isFwdSolenoidBlackListed() { 155 int blackList = getPCMSolenoidBlackList(); 156 return (blackList & m_forwardMask) != 0; 157 } 158 159 /** 160 * Check if the reverse solenoid is blacklisted. If a solenoid is shorted, it is added to the 161 * blacklist and disabled until power cycle, or until faults are cleared. 162 * 163 * @return If solenoid is disabled due to short. 164 * @see #clearAllPCMStickyFaults() 165 */ 166 public boolean isRevSolenoidBlackListed() { 167 int blackList = getPCMSolenoidBlackList(); 168 return (blackList & m_reverseMask) != 0; 169 } 170 171 /* 172 * Live Window code, only does anything if live window is activated. 173 */ 174 public String getSmartDashboardType() { 175 return "Double Solenoid"; 176 } 177 178 private ITable m_table; 179 private ITableListener m_tableListener; 180 181 @Override 182 public void initTable(ITable subtable) { 183 m_table = subtable; 184 updateTable(); 185 } 186 187 @Override 188 public ITable getTable() { 189 return m_table; 190 } 191 192 @Override 193 public void updateTable() { 194 if (m_table != null) { 195 m_table.putString("Value", get().name().substring(1)); 196 } 197 } 198 199 @Override 200 public void startLiveWindowMode() { 201 set(Value.kOff); // Stop for safety 202 m_tableListener = (source, key, value, isNew) -> { 203 if ("Reverse".equals(value.toString())) { 204 set(Value.kReverse); 205 } else if ("Forward".equals(value.toString())) { 206 set(Value.kForward); 207 } else { 208 set(Value.kOff); 209 } 210 }; 211 m_table.addTableListener("Value", m_tableListener, true); 212 } 213 214 @Override 215 public void stopLiveWindowMode() { 216 set(Value.kOff); // Stop for safety 217 // TODO: Broken, should only remove the listener from "Value" only. 218 m_table.removeTableListener(m_tableListener); 219 } 220}