blob: 898612a317d92f1a8440b850e89ad38d4ce71cf0 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Relay.h"
9
10#include <sstream>
11
12#include "LiveWindow/LiveWindow.h"
13#include "MotorSafetyHelper.h"
14#include "WPIErrors.h"
15
16using namespace frc;
17
18/**
19 * Relay constructor given a channel.
20 *
21 * This code initializes the relay and reserves all resources that need to be
22 * locked. Initially the relay is set to both lines at 0v.
23 *
24 * @param channel The channel number (0-3).
25 * @param direction The direction that the Relay object will control.
26 */
27Relay::Relay(int channel, Relay::Direction direction)
28 : m_channel(channel), m_direction(direction) {
29 std::stringstream ss;
30 if (!SensorBase::CheckRelayChannel(m_channel)) {
31 ss << "Relay Channel " << m_channel;
32 wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, ss.str());
33 return;
34 }
35
36 m_safetyHelper = std::make_unique<MotorSafetyHelper>(this);
37 m_safetyHelper->SetSafetyEnabled(false);
38
39 ss << "relay/" << m_channel;
40 impl = new SimContinuousOutput(ss.str()); // TODO: Allow two different relays
41 // (targetting the different halves
42 // of a relay) to be combined to
43 // control one motor.
44 LiveWindow::GetInstance()->AddActuator("Relay", 1, m_channel, this);
45 go_pos = go_neg = false;
46}
47
48/**
49 * Free the resource associated with a relay.
50 *
51 * The relay channels are set to free and the relay output is turned off.
52 */
53Relay::~Relay() {
54 impl->Set(0);
55 if (m_table != nullptr) m_table->RemoveTableListener(this);
56}
57
58/**
59 * Set the relay state.
60 *
61 * Valid values depend on which directions of the relay are controlled by the
62 * object.
63 *
64 * When set to kBothDirections, the relay can be any of the four states:
65 * 0v-0v, 0v-12v, 12v-0v, 12v-12v
66 *
67 * When set to kForwardOnly or kReverseOnly, you can specify the constant for
68 * the direction or you can simply specify kOff and kOn. Using only kOff and
69 * kOn is recommended.
70 *
71 * @param value The state to set the relay.
72 */
73void Relay::Set(Relay::Value value) {
74 switch (value) {
75 case kOff:
76 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
77 go_pos = false;
78 }
79 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
80 go_neg = false;
81 }
82 break;
83 case kOn:
84 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
85 go_pos = true;
86 }
87 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
88 go_neg = true;
89 }
90 break;
91 case kForward:
92 if (m_direction == kReverseOnly) {
93 wpi_setWPIError(IncompatibleMode);
94 break;
95 }
96 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
97 go_pos = true;
98 }
99 if (m_direction == kBothDirections) {
100 go_neg = false;
101 }
102 break;
103 case kReverse:
104 if (m_direction == kForwardOnly) {
105 wpi_setWPIError(IncompatibleMode);
106 break;
107 }
108 if (m_direction == kBothDirections) {
109 go_pos = false;
110 }
111 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
112 go_neg = true;
113 }
114 break;
115 }
116 impl->Set((go_pos ? 1 : 0) + (go_neg ? -1 : 0));
117}
118
119/**
120 * Get the Relay State
121 *
122 * Gets the current state of the relay.
123 *
124 * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not
125 * kForward/kReverse (per the recommendation in Set).
126 *
127 * @return The current state of the relay as a Relay::Value
128 */
129Relay::Value Relay::Get() const {
130 // TODO: Don't assume that the go_pos and go_neg fields are correct?
131 if ((go_pos || m_direction == kReverseOnly) &&
132 (go_neg || m_direction == kForwardOnly)) {
133 return kOn;
134 } else if (go_pos) {
135 return kForward;
136 } else if (go_neg) {
137 return kReverse;
138 } else {
139 return kOff;
140 }
141}
142
143int Relay::GetChannel() const { return m_channel; }
144
145/**
146 * Set the expiration time for the Relay object.
147 *
148 * @param timeout The timeout (in seconds) for this relay object
149 */
150void Relay::SetExpiration(double timeout) {
151 m_safetyHelper->SetExpiration(timeout);
152}
153
154/**
155 * Return the expiration time for the relay object.
156 *
157 * @return The expiration time value.
158 */
159double Relay::GetExpiration() const { return m_safetyHelper->GetExpiration(); }
160
161/**
162 * Check if the relay object is currently alive or stopped due to a timeout.
163 *
164 * @return a bool value that is true if the motor has NOT timed out and should
165 * still be running.
166 */
167bool Relay::IsAlive() const { return m_safetyHelper->IsAlive(); }
168
169/**
170 * Stop the motor associated with this PWM object.
171 *
172 * This is called by the MotorSafetyHelper object when it has a timeout for this
173 * relay and needs to stop it from running.
174 */
175void Relay::StopMotor() { Set(kOff); }
176
177/**
178 * Enable/disable motor safety for this device
179 *
180 * Turn on and off the motor safety option for this relay object.
181 *
182 * @param enabled True if motor safety is enforced for this object
183 */
184void Relay::SetSafetyEnabled(bool enabled) {
185 m_safetyHelper->SetSafetyEnabled(enabled);
186}
187
188/**
189 * Check if motor safety is enabled for this object.
190 *
191 * @return True if motor safety is enforced for this object
192 */
193bool Relay::IsSafetyEnabled() const {
194 return m_safetyHelper->IsSafetyEnabled();
195}
196
197void Relay::GetDescription(std::ostringstream& desc) const {
198 desc << "Relay " << GetChannel();
199}
200
201void Relay::ValueChanged(ITable* source, llvm::StringRef key,
202 std::shared_ptr<nt::Value> value, bool isNew) {
203 if (!value->IsString()) return;
204 if (value->GetString() == "Off")
205 Set(kOff);
206 else if (value->GetString() == "Forward")
207 Set(kForward);
208 else if (value->GetString() == "Reverse")
209 Set(kReverse);
210}
211
212void Relay::UpdateTable() {
213 if (m_table != nullptr) {
214 if (Get() == kOn) {
215 m_table->PutString("Value", "On");
216 } else if (Get() == kForward) {
217 m_table->PutString("Value", "Forward");
218 } else if (Get() == kReverse) {
219 m_table->PutString("Value", "Reverse");
220 } else {
221 m_table->PutString("Value", "Off");
222 }
223 }
224}
225
226void Relay::StartLiveWindowMode() {
227 if (m_table != nullptr) {
228 m_table->AddTableListener("Value", this, true);
229 }
230}
231
232void Relay::StopLiveWindowMode() {
233 if (m_table != nullptr) {
234 m_table->RemoveTableListener(this);
235 }
236}
237
238std::string Relay::GetSmartDashboardType() const { return "Relay"; }
239
240void Relay::InitTable(std::shared_ptr<ITable> subTable) {
241 m_table = subTable;
242 UpdateTable();
243}
244
245std::shared_ptr<ITable> Relay::GetTable() const { return m_table; }