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 * DoubleSolenoid class for running 2 channels of high voltage Digital Output on the pneumatics 016 * module. 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 implements Sendable, AutoCloseable { 022 /** Possible values for a DoubleSolenoid. */ 023 public enum Value { 024 /** Off position. */ 025 kOff, 026 /** Forward position. */ 027 kForward, 028 /** Reverse position. */ 029 kReverse 030 } 031 032 private final int m_forwardMask; // The mask for the forward channel. 033 private final int m_reverseMask; // The mask for the reverse channel. 034 private final int m_mask; // The channel mask 035 private PneumaticsBase m_module; 036 private final int m_forwardChannel; 037 private final int m_reverseChannel; 038 039 /** 040 * Constructs a double solenoid for a default module of a specific module type. 041 * 042 * @param moduleType The module type to use. 043 * @param forwardChannel The forward channel on the module to control. 044 * @param reverseChannel The reverse channel on the module to control. 045 */ 046 public DoubleSolenoid( 047 final PneumaticsModuleType moduleType, final int forwardChannel, final int reverseChannel) { 048 this(PneumaticsBase.getDefaultForType(moduleType), moduleType, forwardChannel, reverseChannel); 049 } 050 051 /** 052 * Constructs a double solenoid for a specified module of a specific module type. 053 * 054 * @param module The module of the solenoid module to use. 055 * @param moduleType The module type to use. 056 * @param forwardChannel The forward channel on the module to control. 057 * @param reverseChannel The reverse channel on the module to control. 058 */ 059 @SuppressWarnings({"PMD.UseTryWithResources", "this-escape"}) 060 public DoubleSolenoid( 061 final int module, 062 final PneumaticsModuleType moduleType, 063 final int forwardChannel, 064 final int reverseChannel) { 065 m_module = PneumaticsBase.getForType(module, moduleType); 066 boolean allocatedSolenoids = false; 067 boolean successfulCompletion = false; 068 069 m_forwardChannel = forwardChannel; 070 m_reverseChannel = reverseChannel; 071 072 m_forwardMask = 1 << forwardChannel; 073 m_reverseMask = 1 << reverseChannel; 074 m_mask = m_forwardMask | m_reverseMask; 075 076 try { 077 if (!m_module.checkSolenoidChannel(forwardChannel)) { 078 throw new IllegalArgumentException("Channel " + forwardChannel + " out of range"); 079 } 080 081 if (!m_module.checkSolenoidChannel(reverseChannel)) { 082 throw new IllegalArgumentException("Channel " + reverseChannel + " out of range"); 083 } 084 085 int allocMask = m_module.checkAndReserveSolenoids(m_mask); 086 if (allocMask != 0) { 087 if (allocMask == m_mask) { 088 throw new AllocationException( 089 "Channels " + forwardChannel + " and " + reverseChannel + " already allocated"); 090 } else if (allocMask == m_forwardMask) { 091 throw new AllocationException("Channel " + forwardChannel + " already allocated"); 092 } else { 093 throw new AllocationException("Channel " + reverseChannel + " already allocated"); 094 } 095 } 096 allocatedSolenoids = true; 097 098 HAL.report( 099 tResourceType.kResourceType_Solenoid, forwardChannel + 1, m_module.getModuleNumber() + 1); 100 HAL.report( 101 tResourceType.kResourceType_Solenoid, reverseChannel + 1, m_module.getModuleNumber() + 1); 102 SendableRegistry.addLW(this, "DoubleSolenoid", m_module.getModuleNumber(), forwardChannel); 103 successfulCompletion = true; 104 } finally { 105 if (!successfulCompletion) { 106 if (allocatedSolenoids) { 107 m_module.unreserveSolenoids(m_mask); 108 } 109 m_module.close(); 110 } 111 } 112 } 113 114 @Override 115 public synchronized void close() { 116 SendableRegistry.remove(this); 117 m_module.unreserveSolenoids(m_mask); 118 m_module.close(); 119 m_module = null; 120 } 121 122 /** 123 * Set the value of a solenoid. 124 * 125 * @param value The value to set (Off, Forward, Reverse) 126 */ 127 public void set(final Value value) { 128 int setValue; 129 130 switch (value) { 131 case kOff: 132 setValue = 0; 133 break; 134 case kForward: 135 setValue = m_forwardMask; 136 break; 137 case kReverse: 138 setValue = m_reverseMask; 139 break; 140 default: 141 throw new AssertionError("Illegal value: " + value); 142 } 143 144 m_module.setSolenoids(m_mask, setValue); 145 } 146 147 /** 148 * Read the current value of the solenoid. 149 * 150 * @return The current value of the solenoid. 151 */ 152 public Value get() { 153 int values = m_module.getSolenoids(); 154 155 if ((values & m_forwardMask) != 0) { 156 return Value.kForward; 157 } else if ((values & m_reverseMask) != 0) { 158 return Value.kReverse; 159 } else { 160 return Value.kOff; 161 } 162 } 163 164 /** 165 * Toggle the value of the solenoid. 166 * 167 * <p>If the solenoid is set to forward, it'll be set to reverse. If the solenoid is set to 168 * reverse, it'll be set to forward. If the solenoid is set to off, nothing happens. 169 */ 170 public void toggle() { 171 Value value = get(); 172 173 if (value == Value.kForward) { 174 set(Value.kReverse); 175 } else if (value == Value.kReverse) { 176 set(Value.kForward); 177 } 178 } 179 180 /** 181 * Get the forward channel. 182 * 183 * @return the forward channel. 184 */ 185 public int getFwdChannel() { 186 return m_forwardChannel; 187 } 188 189 /** 190 * Get the reverse channel. 191 * 192 * @return the reverse channel. 193 */ 194 public int getRevChannel() { 195 return m_reverseChannel; 196 } 197 198 /** 199 * Check if the forward solenoid is Disabled. If a solenoid is shorted, it is added to the 200 * DisabledList and disabled until power cycle, or until faults are cleared. 201 * 202 * @return If solenoid is disabled due to short. 203 */ 204 public boolean isFwdSolenoidDisabled() { 205 return (m_module.getSolenoidDisabledList() & m_forwardMask) != 0; 206 } 207 208 /** 209 * Check if the reverse solenoid is Disabled. If a solenoid is shorted, it is added to the 210 * DisabledList and disabled until power cycle, or until faults are cleared. 211 * 212 * @return If solenoid is disabled due to short. 213 */ 214 public boolean isRevSolenoidDisabled() { 215 return (m_module.getSolenoidDisabledList() & m_reverseMask) != 0; 216 } 217 218 @Override 219 public void initSendable(SendableBuilder builder) { 220 builder.setSmartDashboardType("Double Solenoid"); 221 builder.setActuator(true); 222 builder.setSafeState(() -> set(Value.kOff)); 223 builder.addStringProperty( 224 "Value", 225 () -> get().name().substring(1), 226 value -> { 227 if ("Forward".equals(value)) { 228 set(Value.kForward); 229 } else if ("Reverse".equals(value)) { 230 set(Value.kReverse); 231 } else { 232 set(Value.kOff); 233 } 234 }); 235 } 236}