001 /*----------------------------------------------------------------------------*/ 002 /* Copyright (c) FIRST 2008-2012. All Rights Reserved. */ 003 /* Open Source Software - may be modified and shared by FRC teams. The code */ 004 /* must be accompanied by the FIRST BSD license file in the root directory of */ 005 /* the project. */ 006 /*----------------------------------------------------------------------------*/ 007 008 package edu.wpi.first.wpilibj; 009 010 import edu.wpi.first.wpilibj.communication.UsageReporting; 011 import edu.wpi.first.wpilibj.fpga.tDIO; 012 import edu.wpi.first.wpilibj.livewindow.LiveWindow; 013 import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable; 014 import edu.wpi.first.wpilibj.parsing.IDeviceController; 015 import edu.wpi.first.wpilibj.tables.ITable; 016 import edu.wpi.first.wpilibj.tables.ITableListener; 017 import edu.wpi.first.wpilibj.util.AllocationException; 018 import edu.wpi.first.wpilibj.util.CheckedAllocationException; 019 020 /** 021 * Class for VEX Robotics Spike style relay outputs. 022 * Relays are intended to be connected to Spikes or similar relays. The relay channels controls 023 * a pair of pins that are either both off, one on, the other on, or both on. This translates into 024 * two Spike outputs at 0v, one at 12v and one at 0v, one at 0v and the other at 12v, or two 025 * Spike outputs at 12V. This allows off, full forward, or full reverse control of motors without 026 * variable speed. It also allows the two channels (forward and reverse) to be used independently 027 * for something that does not care about voltage polarity (like a solenoid). 028 */ 029 public class Relay extends SensorBase implements IDeviceController, LiveWindowSendable { 030 031 /** 032 * This class represents errors in trying to set relay values contradictory 033 * to the direction to which the relay is set. 034 */ 035 public class InvalidValueException extends RuntimeException { 036 037 /** 038 * Create a new exception with the given message 039 * @param message the message to pass with the exception 040 */ 041 public InvalidValueException(String message) { 042 super(message); 043 } 044 } 045 046 /** 047 * The state to drive a Relay to. 048 */ 049 public static class Value { 050 051 /** 052 * The integer value representing this enumeration 053 */ 054 public final int value; 055 static final int kOff_val = 0; 056 static final int kOn_val = 1; 057 static final int kForward_val = 2; 058 static final int kReverse_val = 3; 059 /** 060 * value: off 061 */ 062 public static final Value kOff = new Value(kOff_val); 063 /** 064 * value: on for relays with defined direction 065 */ 066 public static final Value kOn = new Value(kOn_val); 067 /** 068 * value: forward 069 */ 070 public static final Value kForward = new Value(kForward_val); 071 /** 072 * value: reverse 073 */ 074 public static final Value kReverse = new Value(kReverse_val); 075 076 private Value(int value) { 077 this.value = value; 078 } 079 } 080 081 /** 082 * The Direction(s) that a relay is configured to operate in. 083 */ 084 public static class Direction { 085 086 /** 087 * The integer value representing this enumeration 088 */ 089 public final int value; 090 static final int kBoth_val = 0; 091 static final int kForward_val = 1; 092 static final int kReverse_val = 2; 093 /** 094 * direction: both directions are valid 095 */ 096 public static final Direction kBoth = new Direction(kBoth_val); 097 /** 098 * direction: Only forward is valid 099 */ 100 public static final Direction kForward = new Direction(kForward_val); 101 /** 102 * direction: only reverse is valid 103 */ 104 public static final Direction kReverse = new Direction(kReverse_val); 105 106 private Direction(int value) { 107 this.value = value; 108 } 109 } 110 private int m_channel; 111 private Direction m_direction; 112 private DigitalModule m_module; 113 private static Resource relayChannels = new Resource(tDIO.kNumSystems * kRelayChannels * 2); 114 115 /** 116 * Common relay initialization method. 117 * This code is common to all Relay constructors and initializes the relay and reserves 118 * all resources that need to be locked. Initially the relay is set to both lines at 0v. 119 * @param moduleNumber The number of the digital module to use. 120 */ 121 private void initRelay(final int moduleNumber) { 122 SensorBase.checkRelayModule(moduleNumber); 123 SensorBase.checkRelayChannel(m_channel); 124 try { 125 if (m_direction == Direction.kBoth || m_direction == Direction.kForward) { 126 relayChannels.allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2); 127 UsageReporting.report(UsageReporting.kResourceType_Relay, m_channel, moduleNumber-1); 128 } 129 if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) { 130 relayChannels.allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2 + 1); 131 UsageReporting.report(UsageReporting.kResourceType_Relay, m_channel+128, moduleNumber-1); 132 } 133 } catch (CheckedAllocationException e) { 134 throw new AllocationException("Relay channel " + m_channel + " on module " + moduleNumber + " is already allocated"); 135 } 136 m_module = DigitalModule.getInstance(moduleNumber); 137 m_module.setRelayForward(m_channel, false); 138 m_module.setRelayReverse(m_channel, false); 139 LiveWindow.addActuator("Relay", moduleNumber, m_channel, this); 140 } 141 142 143 /** 144 * Relay constructor given the module and the channel. 145 * @param moduleNumber The number of the digital module to use. 146 * @param channel The channel number within the module for this relay. 147 * @param direction The direction that the Relay object will control. 148 */ 149 public Relay(final int moduleNumber, final int channel, Direction direction) { 150 if (direction == null) 151 throw new NullPointerException("Null Direction was given"); 152 m_channel = channel; 153 m_direction = direction; 154 initRelay(moduleNumber); 155 } 156 157 /** 158 * Relay constructor given a channel only where the default digital module is used. 159 * @param channel The channel number within the default module for this relay. 160 * @param direction The direction that the Relay object will control. 161 */ 162 public Relay(final int channel, Direction direction) { 163 if (direction == null) 164 throw new NullPointerException("Null Direction was given"); 165 m_channel = channel; 166 m_direction = direction; 167 initRelay(getDefaultDigitalModule()); 168 } 169 170 /** 171 * Relay constructor given the module and the channel, allowing both directions. 172 * @param moduleNumber The number of the digital module to use. 173 * @param channel The channel number within the module for this relay. 174 */ 175 public Relay(final int moduleNumber, final int channel) { 176 m_channel = channel; 177 m_direction = Direction.kBoth; 178 initRelay(moduleNumber); 179 } 180 181 /** 182 * Relay constructor given a channel only where the default digital module is used, 183 * allowing both directions. 184 * @param channel The channel number within the default module for this relay. 185 */ 186 public Relay(final int channel) { 187 m_channel = channel; 188 m_direction = Direction.kBoth; 189 initRelay(getDefaultDigitalModule()); 190 } 191 192 public void free() { 193 m_module.setRelayForward(m_channel, false); 194 m_module.setRelayReverse(m_channel, false); 195 196 if (m_direction == Direction.kBoth || m_direction == Direction.kForward) { 197 relayChannels.free(((m_module.getModuleNumber() - 1) * kRelayChannels + m_channel - 1) * 2); 198 } 199 if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) { 200 relayChannels.free(((m_module.getModuleNumber() - 1) * kRelayChannels + m_channel - 1) * 2 + 1); 201 } 202 } 203 204 /** 205 * Set the relay state. 206 * 207 * Valid values depend on which directions of the relay are controlled by the object. 208 * 209 * When set to kBothDirections, the relay can be set to any of the four states: 210 * 0v-0v, 12v-0v, 0v-12v, 12v-12v 211 * 212 * When set to kForwardOnly or kReverseOnly, you can specify the constant for the 213 * direction or you can simply specify kOff_val and kOn_val. Using only kOff_val and kOn_val is 214 * recommended. 215 * 216 * @param value The state to set the relay. 217 */ 218 public void set(Value value) { 219 switch (value.value) { 220 case Value.kOff_val: 221 if (m_direction == Direction.kBoth || m_direction == Direction.kForward) { 222 m_module.setRelayForward(m_channel, false); 223 } 224 if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) { 225 m_module.setRelayReverse(m_channel, false); 226 } 227 break; 228 case Value.kOn_val: 229 if (m_direction == Direction.kBoth || m_direction == Direction.kForward) { 230 m_module.setRelayForward(m_channel, true); 231 } 232 if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) { 233 m_module.setRelayReverse(m_channel, true); 234 } 235 break; 236 case Value.kForward_val: 237 if (m_direction == Direction.kReverse) 238 throw new InvalidValueException("A relay configured for reverse cannot be set to forward"); 239 if (m_direction == Direction.kBoth || m_direction == Direction.kForward) { 240 m_module.setRelayForward(m_channel, true); 241 } 242 if (m_direction == Direction.kBoth) { 243 m_module.setRelayReverse(m_channel, false); 244 } 245 break; 246 case Value.kReverse_val: 247 if (m_direction == Direction.kForward) 248 throw new InvalidValueException("A relay configured for forward cannot be set to reverse"); 249 if (m_direction == Direction.kBoth) { 250 m_module.setRelayForward(m_channel, false); 251 } 252 if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) { 253 m_module.setRelayReverse(m_channel, true); 254 } 255 break; 256 default: 257 //Cannot hit this, limited by Value enum 258 } 259 } 260 261 /** 262 * Get the Relay State 263 * 264 * Gets the current state of the relay. 265 * 266 * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not 267 * kForward/kReverse (per the recommendation in Set) 268 * 269 * @return The current state of the relay as a Relay::Value 270 */ 271 public Value get() { 272 if(m_module.getRelayForward(m_channel)) { 273 if(m_module.getRelayReverse(m_channel)) { 274 return Value.kOn; 275 } else { 276 if(m_direction == Direction.kForward) { 277 return Value.kOn; 278 } else { 279 return Value.kForward; 280 } 281 } 282 } else { 283 if(m_module.getRelayReverse(m_channel)) { 284 if(m_direction == Direction.kForward) { 285 return Value.kOn; 286 } else { 287 return Value.kReverse; 288 } 289 } else { 290 return Value.kOff; 291 } 292 } 293 } 294 295 /** 296 * Set the Relay Direction 297 * 298 * Changes which values the relay can be set to depending on which direction is 299 * used 300 * 301 * Valid inputs are kBothDirections, kForwardOnly, and kReverseOnly 302 * 303 *@param direction The direction for the relay to operate in 304 */ 305 public void setDirection(Direction direction) { 306 if (direction == null) 307 throw new NullPointerException("Null Direction was given"); 308 if (m_direction == direction) { 309 return; 310 } 311 312 free(); 313 314 m_direction = direction; 315 316 initRelay(m_module.getModuleNumber()); 317 } 318 319 /* 320 * Live Window code, only does anything if live window is activated. 321 */ 322 public String getSmartDashboardType(){ 323 return "Relay"; 324 } 325 private ITable m_table; 326 private ITableListener m_table_listener; 327 328 /** 329 * {@inheritDoc} 330 */ 331 public void initTable(ITable subtable) { 332 m_table = subtable; 333 updateTable(); 334 } 335 336 /** 337 * {@inheritDoc} 338 */ 339 public ITable getTable(){ 340 return m_table; 341 } 342 343 /** 344 * {@inheritDoc} 345 */ 346 public void updateTable() { 347 if(m_table != null){ 348 if (get() == Value.kOn) { 349 m_table.putString("Value", "On"); 350 } else if (get() == Value.kForward) { 351 m_table.putString("Value", "Forward"); 352 } else if (get() == Value.kReverse) { 353 m_table.putString("Value", "Reverse"); 354 } else { 355 m_table.putString("Value", "Off"); 356 } 357 } 358 } 359 360 /** 361 * {@inheritDoc} 362 */ 363 public void startLiveWindowMode() { 364 m_table_listener = new ITableListener() { 365 public void valueChanged(ITable itable, String key, Object value, boolean bln) { 366 String val = ((String) value); 367 if (val.equals("Off")) { 368 set(Value.kOff); 369 } 370 else if (val.equals("Forward")) { 371 set(Value.kForward); 372 } 373 else if (val.equals("Reverse")) { 374 set(Value.kReverse); 375 } 376 } 377 }; 378 m_table.addTableListener("Value", m_table_listener, true); 379 } 380 381 /** 382 * {@inheritDoc} 383 */ 384 public void stopLiveWindowMode() { 385 // TODO: Broken, should only remove the listener from "Value" only. 386 m_table.removeTableListener(m_table_listener); 387 } 388 }