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}