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    }