Files
allwpilib/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Relay.java
2016-06-29 19:58:14 -06:00

412 lines
11 KiB
Java

/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.RelayJNI;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
import edu.wpi.first.wpilibj.util.AllocationException;
import static java.util.Objects.requireNonNull;
/**
* Class for VEX Robotics Spike style relay outputs. Relays are intended to be connected to Spikes
* or similar relays. The relay channels controls a pair of pins that are either both off, one on,
* the other on, or both on. This translates into two Spike outputs at 0v, one at 12v and one at 0v,
* one at 0v and the other at 12v, or two Spike outputs at 12V. This allows off, full forward, or
* full reverse control of motors without variable speed. It also allows the two channels (forward
* and reverse) to be used independently for something that does not care about voltage polarity
* (like a solenoid).
*/
public class Relay extends SensorBase implements MotorSafety, LiveWindowSendable {
private MotorSafetyHelper m_safetyHelper;
/**
* This class represents errors in trying to set relay values contradictory to the direction to
* which the relay is set.
*/
public class InvalidValueException extends RuntimeException {
/**
* Create a new exception with the given message.
*
* @param message the message to pass with the exception
*/
public InvalidValueException(String message) {
super(message);
}
}
/**
* The state to drive a Relay to.
*/
public enum Value {
/**
* value: off.
*/
kOff(0),
/**
* value: on for relays with defined direction.
*/
kOn(1),
/**
* value: forward.
*/
kForward(2),
/**
* value: reverse.
*/
kReverse(3);
/**
* The integer value representing this enumeration.
*/
@SuppressWarnings("MemberName")
public final int value;
Value(int value) {
this.value = value;
}
}
/**
* The Direction(s) that a relay is configured to operate in.
*/
public enum Direction {
/**
* direction: both directions are valid.
*/
kBoth(0),
/**
* direction: Only forward is valid.
*/
kForward(1),
/**
* direction: only reverse is valid.
*/
kReverse(2);
/**
* The integer value representing this enumeration.
*/
@SuppressWarnings("MemberName")
public final int value;
Direction(int value) {
this.value = value;
}
}
private final int m_channel;
private int m_forwardHandle = 0;
private int m_reverseHandle = 0;
private long m_port;
private Direction m_direction;
/**
* Common relay initialization method. This code is common to all Relay constructors and
* initializes the relay and reserves all resources that need to be locked. Initially the relay is
* set to both lines at 0v.
*/
private void initRelay() {
if (!RelayJNI.checkRelayChannel(m_channel)) {
throw new IndexOutOfBoundsException("Requested relay channel number is out of range.");
}
int portHandle = RelayJNI.getPort((byte)m_channel);
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
m_forwardHandle = RelayJNI.initializeRelayPort(portHandle, true);
UsageReporting.report(tResourceType.kResourceType_Relay, m_channel);
}
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
m_reverseHandle = RelayJNI.initializeRelayPort(portHandle, false);
UsageReporting.report(tResourceType.kResourceType_Relay, m_channel + 128);
}
m_safetyHelper = new MotorSafetyHelper(this);
m_safetyHelper.setSafetyEnabled(false);
LiveWindow.addActuator("Relay", m_channel, this);
}
/**
* Relay constructor given a channel.
*
* @param channel The channel number for this relay (0 - 3).
* @param direction The direction that the Relay object will control.
*/
public Relay(final int channel, Direction direction) {
m_channel = channel;
m_direction = requireNonNull( direction, "Null Direction was given");
initRelay();
set(Value.kOff);
}
/**
* Relay constructor given a channel, allowing both directions.
*
* @param channel The channel number for this relay (0 - 3).
*/
public Relay(final int channel) {
this(channel, Direction.kBoth);
}
@Override
public void free() {
try {
RelayJNI.setRelay(m_forwardHandle, false);
} catch (RuntimeException ex) {
// do nothing. Ignore
}
try {
RelayJNI.setRelay(m_reverseHandle, false);
} catch (RuntimeException ex) {
// do nothing. Ignore
}
RelayJNI.freeRelayPort(m_forwardHandle);
RelayJNI.freeRelayPort(m_reverseHandle);
m_forwardHandle = 0;
m_reverseHandle = 0;
}
/**
* Set the relay state.
*
* <p>Valid values depend on which directions of the relay are controlled by the object.
*
* <p>When set to kBothDirections, the relay can be set to any of the four states: 0v-0v, 12v-0v,
* 0v-12v, 12v-12v
*
* <p>When set to kForwardOnly or kReverseOnly, you can specify the constant for the direction or
* you can simply specify kOff_val and kOn_val. Using only kOff_val and kOn_val is recommended.
*
* @param value The state to set the relay.
*/
public void set(Value value) {
switch (value) {
case kOff:
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
RelayJNI.setRelay(m_forwardHandle, false);
}
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
RelayJNI.setRelay(m_reverseHandle, false);
}
break;
case kOn:
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
RelayJNI.setRelay(m_forwardHandle, true);
}
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
RelayJNI.setRelay(m_reverseHandle, true);
}
break;
case kForward:
if (m_direction == Direction.kReverse) {
throw new InvalidValueException("A relay configured for reverse cannot be set to "
+ "forward");
}
if (m_direction == Direction.kBoth || m_direction == Direction.kForward) {
RelayJNI.setRelay(m_forwardHandle, true);
}
if (m_direction == Direction.kBoth) {
RelayJNI.setRelay(m_reverseHandle, false);
}
break;
case kReverse:
if (m_direction == Direction.kForward) {
throw new InvalidValueException("A relay configured for forward cannot be set to "
+ "reverse");
}
if (m_direction == Direction.kBoth) {
RelayJNI.setRelay(m_forwardHandle, false);
}
if (m_direction == Direction.kBoth || m_direction == Direction.kReverse) {
RelayJNI.setRelay(m_reverseHandle, true);
}
break;
default:
// Cannot hit this, limited by Value enum
}
}
/**
* Get the Relay State.
*
* <p>Gets the current state of the relay.
*
* <p>When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not
* kForward/kReverse (per the recommendation in Set)
*
* @return The current state of the relay as a Relay::Value
*/
public Value get() {
if (RelayJNI.getRelay(m_forwardHandle)) {
if (RelayJNI.getRelay(m_reverseHandle)) {
return Value.kOn;
} else {
if (m_direction == Direction.kForward) {
return Value.kOn;
} else {
return Value.kForward;
}
}
} else {
if (RelayJNI.getRelay(m_reverseHandle)) {
if (m_direction == Direction.kReverse) {
return Value.kOn;
} else {
return Value.kReverse;
}
} else {
return Value.kOff;
}
}
}
/**
* Get the channel number.
*
* @return The channel number.
*/
public int getChannel() {
return m_channel;
}
@Override
public void setExpiration(double timeout) {
m_safetyHelper.setExpiration(timeout);
}
@Override
public double getExpiration() {
return m_safetyHelper.getExpiration();
}
@Override
public boolean isAlive() {
return m_safetyHelper.isAlive();
}
@Override
public void stopMotor() {
set(Value.kOff);
}
@Override
public boolean isSafetyEnabled() {
return m_safetyHelper.isSafetyEnabled();
}
@Override
public void setSafetyEnabled(boolean enabled) {
m_safetyHelper.setSafetyEnabled(enabled);
}
@Override
public String getDescription() {
return "Relay ID " + getChannel();
}
/**
* Set the Relay Direction.
*
* <p>Changes which values the relay can be set to depending on which direction is used
*
* <p>Valid inputs are kBothDirections, kForwardOnly, and kReverseOnly
*
* @param direction The direction for the relay to operate in
*/
public void setDirection(Direction direction) {
if (direction == null) {
throw new NullPointerException("Null Direction was given");
}
if (m_direction == direction) {
return;
}
free();
m_direction = direction;
initRelay();
}
/*
* Live Window code, only does anything if live window is activated.
*/
@Override
public String getSmartDashboardType() {
return "Relay";
}
private ITable m_table;
private ITableListener m_tableListener;
@Override
public void initTable(ITable subtable) {
m_table = subtable;
updateTable();
}
@Override
public ITable getTable() {
return m_table;
}
@Override
public void updateTable() {
if (m_table != null) {
if (get() == Value.kOn) {
m_table.putString("Value", "On");
} else if (get() == Value.kForward) {
m_table.putString("Value", "Forward");
} else if (get() == Value.kReverse) {
m_table.putString("Value", "Reverse");
} else {
m_table.putString("Value", "Off");
}
}
}
@Override
public void startLiveWindowMode() {
m_tableListener = new ITableListener() {
@Override
public void valueChanged(ITable itable, String key, Object value, boolean bln) {
String val = ((String) value);
if (val.equals("Off")) {
set(Value.kOff);
} else if (val.equals("On")) {
set(Value.kOn);
} else if (val.equals("Forward")) {
set(Value.kForward);
} else if (val.equals("Reverse")) {
set(Value.kReverse);
}
}
};
m_table.addTableListener("Value", m_tableListener, true);
}
@Override
public void stopLiveWindowMode() {
// TODO: Broken, should only remove the listener from "Value" only.
m_table.removeTableListener(m_tableListener);
}
}