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 }