001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import edu.wpi.first.hal.FRCNetComm.tResourceType;
008import edu.wpi.first.hal.HAL;
009import edu.wpi.first.hal.util.AllocationException;
010import edu.wpi.first.util.sendable.Sendable;
011import edu.wpi.first.util.sendable.SendableBuilder;
012import edu.wpi.first.util.sendable.SendableRegistry;
013
014/**
015 * Solenoid class for running high voltage Digital Output on a pneumatics module.
016 *
017 * <p>The Solenoid class is typically used for pneumatic solenoids, but could be used for any device
018 * within the current spec of the module.
019 */
020public class Solenoid implements Sendable, AutoCloseable {
021  private final int m_mask; // The channel mask
022  private final int m_channel;
023  private PneumaticsBase m_module;
024
025  /**
026   * Constructs a solenoid for a default module and specified type.
027   *
028   * @param moduleType The module type to use.
029   * @param channel The channel the solenoid is on.
030   */
031  public Solenoid(final PneumaticsModuleType moduleType, final int channel) {
032    this(PneumaticsBase.getDefaultForType(moduleType), moduleType, channel);
033  }
034
035  /**
036   * Constructs a solenoid for a specified module and type.
037   *
038   * @param module The module ID to use.
039   * @param moduleType The module type to use.
040   * @param channel The channel the solenoid is on.
041   */
042  @SuppressWarnings("this-escape")
043  public Solenoid(final int module, final PneumaticsModuleType moduleType, final int channel) {
044    m_module = PneumaticsBase.getForType(module, moduleType);
045
046    m_mask = 1 << channel;
047    m_channel = channel;
048
049    if (!m_module.checkSolenoidChannel(channel)) {
050      m_module.close();
051      throw new IllegalArgumentException("Channel " + channel + " out of range");
052    }
053
054    if (m_module.checkAndReserveSolenoids(m_mask) != 0) {
055      m_module.close();
056      throw new AllocationException("Solenoid already allocated");
057    }
058
059    HAL.report(tResourceType.kResourceType_Solenoid, channel + 1, m_module.getModuleNumber() + 1);
060    SendableRegistry.addLW(this, "Solenoid", m_module.getModuleNumber(), channel);
061  }
062
063  @Override
064  public void close() {
065    SendableRegistry.remove(this);
066    m_module.unreserveSolenoids(m_mask);
067    m_module.close();
068    m_module = null;
069  }
070
071  /**
072   * Set the value of a solenoid.
073   *
074   * @param on True will turn the solenoid output on. False will turn the solenoid output off.
075   */
076  public void set(boolean on) {
077    int value = on ? (0xFFFF & m_mask) : 0;
078    m_module.setSolenoids(m_mask, value);
079  }
080
081  /**
082   * Read the current value of the solenoid.
083   *
084   * @return True if the solenoid output is on or false if the solenoid output is off.
085   */
086  public boolean get() {
087    int currentAll = m_module.getSolenoids();
088    return (currentAll & m_mask) != 0;
089  }
090
091  /**
092   * Toggle the value of the solenoid.
093   *
094   * <p>If the solenoid is set to on, it'll be turned off. If the solenoid is set to off, it'll be
095   * turned on.
096   */
097  public void toggle() {
098    set(!get());
099  }
100
101  /**
102   * Get the channel this solenoid is connected to.
103   *
104   * @return The channel this solenoid is connected to.
105   */
106  public int getChannel() {
107    return m_channel;
108  }
109
110  /**
111   * Check if solenoid is DisabledListed. If a solenoid is shorted, it is added to the Disabled List
112   * and disabled until power cycle, or until faults are cleared.
113   *
114   * @return If solenoid is disabled due to short.
115   */
116  public boolean isDisabled() {
117    return (m_module.getSolenoidDisabledList() & m_mask) != 0;
118  }
119
120  /**
121   * Set the pulse duration in the pneumatics module. This is used in conjunction with the
122   * startPulse method to allow the pneumatics module to control the timing of a pulse.
123   *
124   * <p>On the PCM, the timing can be controlled in 0.01 second increments, with a maximum of 2.55
125   * seconds.
126   *
127   * <p>On the PH, the timing can be controlled in 0.001 second increments, with a maximum of 65.534
128   * seconds.
129   *
130   * @param durationSeconds The duration of the pulse in seconds.
131   * @see #startPulse()
132   */
133  public void setPulseDuration(double durationSeconds) {
134    long durationMS = (long) (durationSeconds * 1000);
135    m_module.setOneShotDuration(m_channel, (int) durationMS);
136  }
137
138  /**
139   * Trigger the pneumatics module to generate a pulse of the duration set in setPulseDuration.
140   *
141   * @see #setPulseDuration(double)
142   */
143  public void startPulse() {
144    m_module.fireOneShot(m_channel);
145  }
146
147  @Override
148  public void initSendable(SendableBuilder builder) {
149    builder.setSmartDashboardType("Solenoid");
150    builder.setActuator(true);
151    builder.setSafeState(() -> set(false));
152    builder.addBooleanProperty("Value", this::get, this::set);
153  }
154}