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