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