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 }