001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. 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
008package edu.wpi.first.wpilibj;
009
010import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
011import edu.wpi.first.wpilibj.hal.HAL;
012import edu.wpi.first.wpilibj.hal.SolenoidJNI;
013import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
014
015/**
016 * DoubleSolenoid class for running 2 channels of high voltage Digital Output on the PCM.
017 *
018 * <p>The DoubleSolenoid class is typically used for pneumatics solenoids that have two positions
019 * controlled by two separate channels.
020 */
021public class DoubleSolenoid extends SolenoidBase implements Sendable {
022  /**
023   * Possible values for a DoubleSolenoid.
024   */
025  public enum Value {
026    kOff,
027    kForward,
028    kReverse
029  }
030
031  private byte m_forwardMask; // The mask for the forward channel.
032  private byte m_reverseMask; // The mask for the reverse channel.
033  private int m_forwardHandle = 0;
034  private int m_reverseHandle = 0;
035
036  /**
037   * Constructor. Uses the default PCM ID (defaults to 0).
038   *
039   * @param forwardChannel The forward channel number on the PCM (0..7).
040   * @param reverseChannel The reverse channel number on the PCM (0..7).
041   */
042  public DoubleSolenoid(final int forwardChannel, final int reverseChannel) {
043    this(SensorBase.getDefaultSolenoidModule(), forwardChannel, reverseChannel);
044  }
045
046  /**
047   * Constructor.
048   *
049   * @param moduleNumber   The module number of the solenoid module to use.
050   * @param forwardChannel The forward channel on the module to control (0..7).
051   * @param reverseChannel The reverse channel on the module to control (0..7).
052   */
053  public DoubleSolenoid(final int moduleNumber, final int forwardChannel,
054                        final int reverseChannel) {
055    super(moduleNumber);
056
057    SensorBase.checkSolenoidModule(m_moduleNumber);
058    SensorBase.checkSolenoidChannel(forwardChannel);
059    SensorBase.checkSolenoidChannel(reverseChannel);
060
061    int portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) forwardChannel);
062    m_forwardHandle = SolenoidJNI.initializeSolenoidPort(portHandle);
063
064    try {
065      portHandle = SolenoidJNI.getPortWithModule((byte) m_moduleNumber, (byte) reverseChannel);
066      m_reverseHandle = SolenoidJNI.initializeSolenoidPort(portHandle);
067    } catch (RuntimeException ex) {
068      // free the forward handle on exception, then rethrow
069      SolenoidJNI.freeSolenoidPort(m_forwardHandle);
070      m_forwardHandle = 0;
071      m_reverseHandle = 0;
072      throw ex;
073    }
074
075    m_forwardMask = (byte) (1 << forwardChannel);
076    m_reverseMask = (byte) (1 << reverseChannel);
077
078    HAL.report(tResourceType.kResourceType_Solenoid, forwardChannel,
079                                   m_moduleNumber);
080    HAL.report(tResourceType.kResourceType_Solenoid, reverseChannel,
081                                   m_moduleNumber);
082    setName("DoubleSolenoid", m_moduleNumber, forwardChannel);
083  }
084
085  /**
086   * Destructor.
087   */
088  @Override
089  public synchronized void free() {
090    super.free();
091    SolenoidJNI.freeSolenoidPort(m_forwardHandle);
092    SolenoidJNI.freeSolenoidPort(m_reverseHandle);
093    super.free();
094  }
095
096  /**
097   * Set the value of a solenoid.
098   *
099   * @param value The value to set (Off, Forward, Reverse)
100   */
101  public void set(final Value value) {
102    boolean forward = false;
103    boolean reverse = false;
104
105    switch (value) {
106      case kOff:
107        forward = false;
108        reverse = false;
109        break;
110      case kForward:
111        forward = true;
112        reverse = false;
113        break;
114      case kReverse:
115        forward = false;
116        reverse = true;
117        break;
118      default:
119        throw new AssertionError("Illegal value: " + value);
120
121    }
122
123    SolenoidJNI.setSolenoid(m_forwardHandle, forward);
124    SolenoidJNI.setSolenoid(m_reverseHandle, reverse);
125  }
126
127  /**
128   * Read the current value of the solenoid.
129   *
130   * @return The current value of the solenoid.
131   */
132  public Value get() {
133    boolean valueForward = SolenoidJNI.getSolenoid(m_forwardHandle);
134    boolean valueReverse = SolenoidJNI.getSolenoid(m_reverseHandle);
135
136    if (valueForward) {
137      return Value.kForward;
138    }
139    if (valueReverse) {
140      return Value.kReverse;
141    }
142    return Value.kOff;
143  }
144
145  /**
146   * Check if the forward solenoid is blacklisted. If a solenoid is shorted, it is added to the
147   * blacklist and disabled until power cycle, or until faults are cleared.
148   *
149   * @return If solenoid is disabled due to short.
150   * @see #clearAllPCMStickyFaults()
151   */
152  public boolean isFwdSolenoidBlackListed() {
153    int blackList = getPCMSolenoidBlackList();
154    return (blackList & m_forwardMask) != 0;
155  }
156
157  /**
158   * Check if the reverse solenoid is blacklisted. If a solenoid is shorted, it is added to the
159   * blacklist and disabled until power cycle, or until faults are cleared.
160   *
161   * @return If solenoid is disabled due to short.
162   * @see #clearAllPCMStickyFaults()
163   */
164  public boolean isRevSolenoidBlackListed() {
165    int blackList = getPCMSolenoidBlackList();
166    return (blackList & m_reverseMask) != 0;
167  }
168
169  @Override
170  public void initSendable(SendableBuilder builder) {
171    builder.setSmartDashboardType("Double Solenoid");
172    builder.setSafeState(() -> set(Value.kOff));
173    builder.addStringProperty("Value", () -> get().name().substring(1), (value) -> {
174      if ("Forward".equals(value)) {
175        set(Value.kForward);
176      } else if ("Reverse".equals(value)) {
177        set(Value.kReverse);
178      } else {
179        set(Value.kOff);
180      }
181    });
182  }
183}