001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2012. 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/*----------------------------------------------------------------------------*/ 007package edu.wpi.first.wpilibj; 008 009import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType; 010import edu.wpi.first.wpilibj.communication.UsageReporting; 011import edu.wpi.first.wpilibj.livewindow.LiveWindow; 012import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 013import edu.wpi.first.wpilibj.tables.ITable; 014import edu.wpi.first.wpilibj.tables.ITableListener; 015import edu.wpi.first.wpilibj.util.AllocationException; 016import edu.wpi.first.wpilibj.util.CheckedAllocationException; 017 018/** 019 * DoubleSolenoid class for running 2 channels of high voltage Digital Output. 020 * 021 * The DoubleSolenoid class is typically used for pneumatics solenoids that 022 * have two positions controlled by two separate channels. 023 */ 024public class DoubleSolenoid extends SolenoidBase implements LiveWindowSendable { 025 026 /** 027 * Possible values for a DoubleSolenoid 028 */ 029 public static class Value { 030 031 public final int value; 032 public static final int kOff_val = 0; 033 public static final int kForward_val = 1; 034 public static final int kReverse_val = 2; 035 public static final Value kOff = new Value(kOff_val); 036 public static final Value kForward = new Value(kForward_val); 037 public static final Value kReverse = new Value(kReverse_val); 038 039 private Value(int value) { 040 this.value = value; 041 } 042 } 043 private int m_forwardChannel; ///< The forward channel on the module to control. 044 private int m_reverseChannel; ///< The reverse channel on the module to control. 045 private byte m_forwardMask; ///< The mask for the forward channel. 046 private byte m_reverseMask; ///< The mask for the reverse channel. 047 048 /** 049 * Common function to implement constructor behavior. 050 */ 051 private synchronized void initSolenoid() { 052 checkSolenoidModule(m_moduleNumber); 053 checkSolenoidChannel(m_forwardChannel); 054 checkSolenoidChannel(m_reverseChannel); 055 056 try { 057 m_allocated.allocate(m_moduleNumber * kSolenoidChannels + m_forwardChannel); 058 } catch (CheckedAllocationException e) { 059 throw new AllocationException( 060 "Solenoid channel " + m_forwardChannel + " on module " + m_moduleNumber + " is already allocated"); 061 } 062 try { 063 m_allocated.allocate(m_moduleNumber * kSolenoidChannels + m_reverseChannel); 064 } catch (CheckedAllocationException e) { 065 throw new AllocationException( 066 "Solenoid channel " + m_reverseChannel + " on module " + m_moduleNumber + " is already allocated"); 067 } 068 m_forwardMask = (byte) (1 << m_forwardChannel); 069 m_reverseMask = (byte) (1 << m_reverseChannel); 070 071 UsageReporting.report(tResourceType.kResourceType_Solenoid, m_forwardChannel, m_moduleNumber); 072 UsageReporting.report(tResourceType.kResourceType_Solenoid, m_reverseChannel, m_moduleNumber); 073 LiveWindow.addActuator("DoubleSolenoid", m_moduleNumber, m_forwardChannel, this); 074 } 075 076 /** 077 * Constructor. 078 * Uses the default PCM ID of 0 079 * @param forwardChannel The forward channel number on the PCM (0..7). 080 * @param reverseChannel The reverse channel number on the PCM (0..7). 081 */ 082 public DoubleSolenoid(final int forwardChannel, final int reverseChannel) { 083 super(getDefaultSolenoidModule()); 084 m_forwardChannel = forwardChannel; 085 m_reverseChannel = reverseChannel; 086 initSolenoid(); 087 } 088 089 /** 090 * Constructor. 091 * 092 * @param moduleNumber The module number of the solenoid module to use. 093 * @param forwardChannel The forward channel on the module to control (0..7). 094 * @param reverseChannel The reverse channel on the module to control (0..7). 095 */ 096 public DoubleSolenoid(final int moduleNumber, final int forwardChannel, final int reverseChannel) { 097 super(moduleNumber); 098 m_forwardChannel = forwardChannel; 099 m_reverseChannel = reverseChannel; 100 initSolenoid(); 101 } 102 103 /** 104 * Destructor. 105 */ 106 public synchronized void free() { 107 m_allocated.free(m_moduleNumber * kSolenoidChannels + m_forwardChannel); 108 m_allocated.free(m_moduleNumber * kSolenoidChannels + m_reverseChannel); 109 } 110 111 /** 112 * Set the value of a solenoid. 113 * 114 * @param value The value to set (Off, Forward, Reverse) 115 */ 116 public void set(final Value value) { 117 byte rawValue = 0; 118 119 switch (value.value) { 120 case Value.kOff_val: 121 rawValue = 0x00; 122 break; 123 case Value.kForward_val: 124 rawValue = m_forwardMask; 125 break; 126 case Value.kReverse_val: 127 rawValue = m_reverseMask; 128 break; 129 } 130 131 set(rawValue, m_forwardMask | m_reverseMask); 132 } 133 134 /** 135 * Read the current value of the solenoid. 136 * 137 * @return The current value of the solenoid. 138 */ 139 public Value get() { 140 byte value = getAll(); 141 142 if ((value & m_forwardMask) != 0) return Value.kForward; 143 if ((value & m_reverseMask) != 0) return Value.kReverse; 144 return Value.kOff; 145 } 146 /** 147 * Check if the forward solenoid is blacklisted. 148 * If a solenoid is shorted, it is added to the blacklist and 149 * disabled until power cycle, or until faults are cleared. 150 * @see #clearAllPCMStickyFaults() 151 * 152 * @return If solenoid is disabled due to short. 153 */ 154 public boolean isFwdSolenoidBlackListed() { 155 int blackList = getPCMSolenoidBlackList(); 156 return ((blackList & m_forwardMask) != 0); 157 } 158 /** 159 * Check if the reverse solenoid is blacklisted. 160 * If a solenoid is shorted, it is added to the blacklist and 161 * disabled until power cycle, or until faults are cleared. 162 * @see #clearAllPCMStickyFaults() 163 * 164 * @return If solenoid is disabled due to short. 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 private ITable m_table; 178 private ITableListener m_table_listener; 179 180 /** 181 * {@inheritDoc} 182 */ 183 public void initTable(ITable subtable) { 184 m_table = subtable; 185 updateTable(); 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 public ITable getTable() { 192 return m_table; 193 } 194 195 /** 196 * {@inheritDoc} 197 */ 198 public void updateTable() { 199 if (m_table != null) { 200 //TODO: this is bad 201 m_table.putString("Value", (get() == Value.kForward ? "Forward" : (get() == Value.kReverse ? "Reverse" : "Off"))); 202 } 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 public void startLiveWindowMode() { 209 set(Value.kOff); // Stop for safety 210 m_table_listener = new ITableListener() { 211 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 212 //TODO: this is bad also 213 if (value.toString().equals("Reverse")) 214 set(Value.kReverse); 215 else if (value.toString().equals("Forward")) 216 set(Value.kForward); 217 else 218 set(Value.kOff); 219 } 220 }; 221 m_table.addTableListener("Value", m_table_listener, true); 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 public void stopLiveWindowMode() { 228 set(Value.kOff); // Stop for safety 229 // TODO: Broken, should only remove the listener from "Value" only. 230 m_table.removeTableListener(m_table_listener); 231 } 232}