# validated: 2015-12-22 DS de39877 athena/java/edu/wpi/first/wpilibj/Relay.java
#----------------------------------------------------------------------------
# Copyright (c) FIRST 2008-2012. 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.
#----------------------------------------------------------------------------
import hal
import weakref
from .livewindow import LiveWindow
from .livewindowsendable import LiveWindowSendable
from .motorsafety import MotorSafety
from .resource import Resource
from .sensorbase import SensorBase
__all__ = ["Relay"]
def _freeRelay(port):
hal.setRelayForward(port, False)
hal.setRelayReverse(port, False)
hal.freeDIO(port)
[docs]class Relay(SensorBase, LiveWindowSendable, MotorSafety):
"""Controls 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).
.. not_implemented: initRelay
"""
[docs] class Value:
"""The state to drive a Relay to."""
#: Off
kOff = 0
#: On for relays with defined direction
kOn = 1
#: Forward
kForward = 2
#: Reverse
kReverse = 3
[docs] class Direction:
"""The Direction(s) that a relay is configured to operate in."""
#: Both directions are valid
kBoth = 0
#: Only forward is valid
kForward = 1
#: Only reverse is valid
kReverse = 2
relayChannels = Resource(SensorBase.kRelayChannels * 2)
def __init__(self, channel, direction=None):
"""Relay constructor given a channel.
Initially the relay is set to both lines at 0v.
:param channel: The channel number for this relay (0-3)
:type channel: int
:param direction: The direction that the Relay object will control.
If not specified, defaults to allowing both directions.
:type direction: :class:`Relay.Direction`
"""
if direction is None:
direction = self.Direction.kBoth
self.channel = channel
self.direction = direction
self._initRelay()
self.set(self.Value.kOff)
MotorSafety.__init__(self)
def _initRelay(self):
SensorBase.checkRelayChannel(self.channel)
try:
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kForward):
Relay.relayChannels.allocate(self, self.channel * 2)
hal.HALReport(hal.HALUsageReporting.kResourceType_Relay,
self.channel)
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kReverse):
Relay.relayChannels.allocate(self, self.channel * 2 + 1)
hal.HALReport(hal.HALUsageReporting.kResourceType_Relay,
self.channel + 128)
except IndexError as e:
raise IndexError("Relay channel %d is already allocated" % self.channel) from e
self._port = hal.initializeDigitalPort(hal.getPort(self.channel))
self.__finalizer = weakref.finalize(self, _freeRelay, self._port)
self.setSafetyEnabled(False)
LiveWindow.addActuatorChannel("Relay", self.channel, self)
@property
def port(self):
if not self.__finalizer.alive:
raise ValueError("Cannot use relay after free() has been called")
return self._port
[docs] def free(self):
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kForward):
Relay.relayChannels.free(self.channel*2)
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kReverse):
Relay.relayChannels.free(self.channel*2 + 1)
LiveWindow.removeComponent(self)
self.__finalizer()
[docs] def set(self, value):
"""Set the relay state.
Valid values depend on which directions of the relay are controlled by
the object.
When set to kBothDirections, the relay can be set to any of the four
states: 0v-0v, 12v-0v, 0v-12v, 12v-12v
When set to kForwardOnly or kReverseOnly, you can specify the constant
for the direction or you can simply specify kOff and kOn. Using only
kOff and kOn is recommended.
:param value: The state to set the relay.
:type value: :class:`Relay.Value`
"""
if value == self.Value.kOff:
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kForward):
hal.setRelayForward(self.port, False)
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kReverse):
hal.setRelayReverse(self.port, False)
elif value == self.Value.kOn:
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kForward):
hal.setRelayForward(self.port, True)
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kReverse):
hal.setRelayReverse(self.port, True)
elif value == self.Value.kForward:
if self.direction == self.Direction.kReverse:
raise ValueError("A relay configured for reverse cannot be set to forward")
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kForward):
hal.setRelayForward(self.port, True)
if self.direction == self.Direction.kBoth:
hal.setRelayReverse(self.port, False)
elif value == self.Value.kReverse:
if self.direction == self.Direction.kForward:
raise ValueError("A relay configured for forward cannot be set to reverse")
if self.direction == self.Direction.kBoth:
hal.setRelayForward(self.port, False)
if (self.direction == self.Direction.kBoth or
self.direction == self.Direction.kReverse):
hal.setRelayReverse(self.port, True)
else:
raise ValueError("Invalid value argument '%s'" % value)
[docs] def get(self):
"""Get the Relay State
Gets the current state of the relay.
When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff
not kForward/kReverse (per the recommendation in Set)
:returns: The current state of the relay
:rtype: :class:`Relay.Value`
"""
if hal.getRelayForward(self.port):
if hal.getRelayReverse(self.port):
return self.Value.kOn
else:
if self.direction == self.Direction.kForward:
return self.Value.kOn
else:
return self.Value.kForward
else:
if hal.getRelayReverse(self.port):
if self.direction == self.Direction.kReverse:
return self.Value.kOn
else:
return self.Value.kReverse
else:
return self.Value.kOff
[docs] def getChannel(self):
"""
Get the channel number.
:returns: The channel number.
"""
return self.channel
[docs] def stopMotor(self):
self.set(self.Value.kOff)
[docs] def getDescription(self):
return "Relay ID {}".format(self.getChannel())
[docs] def setDirection(self, direction):
"""Set the Relay Direction.
Changes which values the relay can be set to depending on which
direction is used.
Valid inputs are kBothDirections, kForwardOnly, and kReverseOnly.
:param direction: The direction for the relay to operate in
:type direction: :class:`Relay.Direction`
"""
if self.direction == direction:
return
if direction not in [self.Direction.kBoth,
self.Direction.kForward,
self.Direction.kReverse]:
raise ValueError("Invalid direction argument '%s'" % direction)
self.free()
self.direction = direction
self._initRelay()
# Live Window code, only does anything if live window is activated.
def getSmartDashboardType(self):
return "Relay"
def updateTable(self):
table = self.getTable()
if table is None:
return
v = self.get()
if v == self.Value.kOn:
table.putString("Value", "On")
elif v == self.Value.kForward:
table.putString("Value", "Forward")
elif v == self.Value.kReverse:
table.putString("Value", "Reverse")
else:
table.putString("Value", "Off")
def valueChanged(self, itable, key, value, bln):
if value == "Off":
self.set(self.Value.kOff)
elif value == "On":
self.set(self.Value.kOn)
elif value == "Forward":
self.set(self.Value.kForward)
elif value == "Reverse":
self.set(self.Value.kReverse)