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 /*----------------------------------------------------------------------------*/ 007 package edu.wpi.first.wpilibj; 008 009 import edu.wpi.first.wpilibj.communication.UsageReporting; 010 import edu.wpi.first.wpilibj.livewindow.LiveWindow; 011 import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 012 import edu.wpi.first.wpilibj.tables.ITable; 013 import edu.wpi.first.wpilibj.tables.ITableListener; 014 import edu.wpi.first.wpilibj.util.AllocationException; 015 import edu.wpi.first.wpilibj.util.CheckedAllocationException; 016 017 /** 018 * DoubleSolenoid class for running 2 channels of high voltage Digital Output 019 * (9472 module). 020 * 021 * The DoubleSolenoid class is typically used for pneumatics solenoids that 022 * have two positions controlled by two separate channels. 023 */ 024 public 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 - 1) * kSolenoidChannels + m_forwardChannel - 1); 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 - 1) * kSolenoidChannels + m_reverseChannel - 1); 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 - 1)); 069 m_reverseMask = (byte) (1 << (m_reverseChannel - 1)); 070 071 UsageReporting.report(UsageReporting.kResourceType_Solenoid, m_forwardChannel, m_moduleNumber-1); 072 UsageReporting.report(UsageReporting.kResourceType_Solenoid, m_reverseChannel, m_moduleNumber-1); 073 LiveWindow.addActuator("DoubleSolenoid", m_moduleNumber, m_forwardChannel, this); 074 } 075 076 /** 077 * Constructor. 078 * 079 * @param forwardChannel The forward channel on the module to control. 080 * @param reverseChannel The reverse channel on the module to control. 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. 094 * @param reverseChannel The reverse channel on the module to control. 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 - 1) * kSolenoidChannels + m_forwardChannel - 1); 108 m_allocated.free((m_moduleNumber - 1) * kSolenoidChannels + m_reverseChannel - 1); 109 } 110 111 /** 112 * Set the value of a solenoid. 113 * 114 * @param value Move the solenoid to forward, reverse, or don't move it. 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 /* 148 * Live Window code, only does anything if live window is activated. 149 */ 150 public String getSmartDashboardType(){ 151 return "Double Solenoid"; 152 } 153 private ITable m_table; 154 private ITableListener m_table_listener; 155 156 /** 157 * {@inheritDoc} 158 */ 159 public void initTable(ITable subtable) { 160 m_table = subtable; 161 updateTable(); 162 } 163 164 /** 165 * {@inheritDoc} 166 */ 167 public ITable getTable(){ 168 return m_table; 169 } 170 171 /** 172 * {@inheritDoc} 173 */ 174 public void updateTable() { 175 if (m_table != null) { 176 //TODO: this is bad 177 m_table.putString("Value", (get() == Value.kForward ? "Forward" : (get() == Value.kReverse ? "Reverse" : "Off"))); 178 } 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 public void startLiveWindowMode() { 185 set(Value.kOff); // Stop for safety 186 m_table_listener = new ITableListener() { 187 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 188 //TODO: this is bad also 189 if (value.toString().equals("Reverse")) 190 set(Value.kReverse); 191 else if (value.toString().equals("Forward")) 192 set(Value.kForward); 193 else 194 set(Value.kOff); 195 } 196 }; 197 m_table.addTableListener("Value", m_table_listener, true); 198 } 199 200 /** 201 * {@inheritDoc} 202 */ 203 public void stopLiveWindowMode() { 204 set(Value.kOff); // Stop for safety 205 // TODO: Broken, should only remove the listener from "Value" only. 206 m_table.removeTableListener(m_table_listener); 207 } 208 }