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  public Solenoid(final int module, final PneumaticsModuleType moduleType, final int channel) {
043    m_module = PneumaticsBase.getForType(module, moduleType);
044
045    m_mask = 1 << channel;
046    m_channel = channel;
047
048    if (!m_module.checkSolenoidChannel(channel)) {
049      m_module.close();
050      throw new IllegalArgumentException("Channel " + channel + " out of range");
051    }
052
053    if (m_module.checkAndReserveSolenoids(m_mask) != 0) {
054      m_module.close();
055      throw new AllocationException("Solenoid already allocated");
056    }
057
058    HAL.report(tResourceType.kResourceType_Solenoid, channel + 1, m_module.getModuleNumber() + 1);
059    SendableRegistry.addLW(this, "Solenoid", m_module.getModuleNumber(), channel);
060  }
061
062  @Override
063  public void close() {
064    SendableRegistry.remove(this);
065    m_module.unreserveSolenoids(m_mask);
066    m_module.close();
067    m_module = null;
068  }
069
070  /**
071   * Set the value of a solenoid.
072   *
073   * @param on True will turn the solenoid output on. False will turn the solenoid output off.
074   */
075  public void set(boolean on) {
076    int value = on ? (0xFFFF & m_mask) : 0;
077    m_module.setSolenoids(m_mask, value);
078  }
079
080  /**
081   * Read the current value of the solenoid.
082   *
083   * @return True if the solenoid output is on or false if the solenoid output is off.
084   */
085  public boolean get() {
086    int currentAll = m_module.getSolenoids();
087    return (currentAll & m_mask) != 0;
088  }
089
090  /**
091   * Toggle the value of the solenoid.
092   *
093   * <p>If the solenoid is set to on, it'll be turned off. If the solenoid is set to off, it'll be
094   * turned on.
095   */
096  public void toggle() {
097    set(!get());
098  }
099
100  /**
101   * Get the channel this solenoid is connected to.
102   *
103   * @return The channel this solenoid is connected to.
104   */
105  public int getChannel() {
106    return m_channel;
107  }
108
109  /**
110   * Check if solenoid is DisabledListed. If a solenoid is shorted, it is added to the Disabled List
111   * and disabled until power cycle, or until faults are cleared.
112   *
113   * @return If solenoid is disabled due to short.
114   */
115  public boolean isDisabled() {
116    return (m_module.getSolenoidDisabledList() & m_mask) != 0;
117  }
118
119  /**
120   * Set the pulse duration in the pneumatics module. This is used in conjunction with the
121   * startPulse method to allow the pneumatics module to control the timing of a pulse.
122   *
123   * <p>On the PCM, the timing can be controlled in 0.01 second increments, with a maximum of 2.55
124   * seconds.
125   *
126   * <p>On the PH, the timing can be controlled in 0.001 second increments, with a maximum of 65.534
127   * seconds.
128   *
129   * @param durationSeconds The duration of the pulse in seconds.
130   * @see #startPulse()
131   */
132  public void setPulseDuration(double durationSeconds) {
133    long durationMS = (long) (durationSeconds * 1000);
134    m_module.setOneShotDuration(m_channel, (int) durationMS);
135  }
136
137  /**
138   * Trigger the pneumatics module to generate a pulse of the duration set in setPulseDuration.
139   *
140   * @see #setPulseDuration(double)
141   */
142  public void startPulse() {
143    m_module.fireOneShot(m_channel);
144  }
145
146  @Override
147  public void initSendable(SendableBuilder builder) {
148    builder.setSmartDashboardType("Solenoid");
149    builder.setActuator(true);
150    builder.setSafeState(() -> set(false));
151    builder.addBooleanProperty("Value", this::get, this::set);
152  }
153}