blob: 33cc8769d1d983b150f15488fd9a19f79b431c14 [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 "HAL/Relay.h"
9#include "Relay.h"
10
11#include <sstream>
12
13#include "HAL/HAL.h"
14#include "HAL/Ports.h"
15#include "LiveWindow/LiveWindow.h"
16#include "MotorSafetyHelper.h"
17#include "WPIErrors.h"
18
19using namespace frc;
20
21/**
22 * Relay constructor given a channel.
23 *
24 * This code initializes the relay and reserves all resources that need to be
25 * locked. Initially the relay is set to both lines at 0v.
26 *
27 * @param channel The channel number (0-3).
28 * @param direction The direction that the Relay object will control.
29 */
30Relay::Relay(int channel, Relay::Direction direction)
31 : m_channel(channel), m_direction(direction) {
32 std::stringstream buf;
33 if (!SensorBase::CheckRelayChannel(m_channel)) {
34 buf << "Relay Channel " << m_channel;
35 wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf.str());
36 return;
37 }
38
39 HAL_PortHandle portHandle = HAL_GetPort(channel);
40
41 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
42 int32_t status = 0;
43 m_forwardHandle = HAL_InitializeRelayPort(portHandle, true, &status);
44 if (status != 0) {
45 wpi_setErrorWithContextRange(status, 0, HAL_GetNumRelayChannels(),
46 channel, HAL_GetErrorMessage(status));
47 m_forwardHandle = HAL_kInvalidHandle;
48 m_reverseHandle = HAL_kInvalidHandle;
49 return;
50 }
51 HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel);
52 }
53 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
54 int32_t status = 0;
55 m_reverseHandle = HAL_InitializeRelayPort(portHandle, false, &status);
56 if (status != 0) {
57 wpi_setErrorWithContextRange(status, 0, HAL_GetNumRelayChannels(),
58 channel, HAL_GetErrorMessage(status));
59 m_forwardHandle = HAL_kInvalidHandle;
60 m_reverseHandle = HAL_kInvalidHandle;
61 return;
62 }
63
64 HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 128);
65 }
66
67 int32_t status = 0;
68 if (m_forwardHandle != HAL_kInvalidHandle) {
69 HAL_SetRelay(m_forwardHandle, false, &status);
70 if (status != 0) {
71 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
72 m_forwardHandle = HAL_kInvalidHandle;
73 m_reverseHandle = HAL_kInvalidHandle;
74 return;
75 }
76 }
77 if (m_reverseHandle != HAL_kInvalidHandle) {
78 HAL_SetRelay(m_reverseHandle, false, &status);
79 if (status != 0) {
80 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
81 m_forwardHandle = HAL_kInvalidHandle;
82 m_reverseHandle = HAL_kInvalidHandle;
83 return;
84 }
85 }
86
87 m_safetyHelper = std::make_unique<MotorSafetyHelper>(this);
88 m_safetyHelper->SetSafetyEnabled(false);
89
90 LiveWindow::GetInstance()->AddActuator("Relay", 1, m_channel, this);
91}
92
93/**
94 * Free the resource associated with a relay.
95 *
96 * The relay channels are set to free and the relay output is turned off.
97 */
98Relay::~Relay() {
99 int32_t status = 0;
100 HAL_SetRelay(m_forwardHandle, false, &status);
101 HAL_SetRelay(m_reverseHandle, false, &status);
102 // ignore errors, as we want to make sure a free happens.
103 if (m_forwardHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_forwardHandle);
104 if (m_reverseHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_reverseHandle);
105
106 if (m_table != nullptr) m_table->RemoveTableListener(this);
107}
108
109/**
110 * Set the relay state.
111 *
112 * Valid values depend on which directions of the relay are controlled by the
113 * object.
114 *
115 * When set to kBothDirections, the relay can be any of the four states:
116 * 0v-0v, 0v-12v, 12v-0v, 12v-12v
117 *
118 * When set to kForwardOnly or kReverseOnly, you can specify the constant for
119 * the direction or you can simply specify kOff and kOn. Using only kOff and
120 * kOn is recommended.
121 *
122 * @param value The state to set the relay.
123 */
124void Relay::Set(Relay::Value value) {
125 if (StatusIsFatal()) return;
126
127 int32_t status = 0;
128
129 switch (value) {
130 case kOff:
131 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
132 HAL_SetRelay(m_forwardHandle, false, &status);
133 }
134 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
135 HAL_SetRelay(m_reverseHandle, false, &status);
136 }
137 break;
138 case kOn:
139 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
140 HAL_SetRelay(m_forwardHandle, true, &status);
141 }
142 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
143 HAL_SetRelay(m_reverseHandle, true, &status);
144 }
145 break;
146 case kForward:
147 if (m_direction == kReverseOnly) {
148 wpi_setWPIError(IncompatibleMode);
149 break;
150 }
151 if (m_direction == kBothDirections || m_direction == kForwardOnly) {
152 HAL_SetRelay(m_forwardHandle, true, &status);
153 }
154 if (m_direction == kBothDirections) {
155 HAL_SetRelay(m_reverseHandle, false, &status);
156 }
157 break;
158 case kReverse:
159 if (m_direction == kForwardOnly) {
160 wpi_setWPIError(IncompatibleMode);
161 break;
162 }
163 if (m_direction == kBothDirections) {
164 HAL_SetRelay(m_forwardHandle, false, &status);
165 }
166 if (m_direction == kBothDirections || m_direction == kReverseOnly) {
167 HAL_SetRelay(m_reverseHandle, true, &status);
168 }
169 break;
170 }
171
172 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
173}
174
175/**
176 * Get the Relay State
177 *
178 * Gets the current state of the relay.
179 *
180 * When set to kForwardOnly or kReverseOnly, value is returned as kOn/kOff not
181 * kForward/kReverse (per the recommendation in Set)
182 *
183 * @return The current state of the relay as a Relay::Value
184 */
185Relay::Value Relay::Get() const {
186 int32_t status;
187
188 if (HAL_GetRelay(m_forwardHandle, &status)) {
189 if (HAL_GetRelay(m_reverseHandle, &status)) {
190 return kOn;
191 } else {
192 if (m_direction == kForwardOnly) {
193 return kOn;
194 } else {
195 return kForward;
196 }
197 }
198 } else {
199 if (HAL_GetRelay(m_reverseHandle, &status)) {
200 if (m_direction == kReverseOnly) {
201 return kOn;
202 } else {
203 return kReverse;
204 }
205 } else {
206 return kOff;
207 }
208 }
209
210 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
211}
212
213int Relay::GetChannel() const { return m_channel; }
214
215/**
216 * Set the expiration time for the Relay object
217 * @param timeout The timeout (in seconds) for this relay object
218 */
219void Relay::SetExpiration(double timeout) {
220 m_safetyHelper->SetExpiration(timeout);
221}
222
223/**
224 * Return the expiration time for the relay object.
225 * @return The expiration time value.
226 */
227double Relay::GetExpiration() const { return m_safetyHelper->GetExpiration(); }
228
229/**
230 * Check if the relay object is currently alive or stopped due to a timeout.
231 *
232 * @return a bool value that is true if the motor has NOT timed out and should
233 * still be running.
234 */
235bool Relay::IsAlive() const { return m_safetyHelper->IsAlive(); }
236
237/**
238 * Stop the motor associated with this PWM object.
239 *
240 * This is called by the MotorSafetyHelper object when it has a timeout for this
241 * relay and needs to stop it from running.
242 */
243void Relay::StopMotor() { Set(kOff); }
244
245/**
246 * Enable/disable motor safety for this device.
247 *
248 * Turn on and off the motor safety option for this relay object.
249 *
250 * @param enabled True if motor safety is enforced for this object
251 */
252void Relay::SetSafetyEnabled(bool enabled) {
253 m_safetyHelper->SetSafetyEnabled(enabled);
254}
255
256/**
257 * Check if motor safety is enabled for this object.
258 *
259 * @returns True if motor safety is enforced for this object
260 */
261bool Relay::IsSafetyEnabled() const {
262 return m_safetyHelper->IsSafetyEnabled();
263}
264
265void Relay::GetDescription(std::ostringstream& desc) const {
266 desc << "Relay " << GetChannel();
267}
268
269void Relay::ValueChanged(ITable* source, llvm::StringRef key,
270 std::shared_ptr<nt::Value> value, bool isNew) {
271 if (!value->IsString()) return;
272 if (value->GetString() == "Off")
273 Set(kOff);
274 else if (value->GetString() == "Forward")
275 Set(kForward);
276 else if (value->GetString() == "Reverse")
277 Set(kReverse);
278 else if (value->GetString() == "On")
279 Set(kOn);
280}
281
282void Relay::UpdateTable() {
283 if (m_table != nullptr) {
284 if (Get() == kOn) {
285 m_table->PutString("Value", "On");
286 } else if (Get() == kForward) {
287 m_table->PutString("Value", "Forward");
288 } else if (Get() == kReverse) {
289 m_table->PutString("Value", "Reverse");
290 } else {
291 m_table->PutString("Value", "Off");
292 }
293 }
294}
295
296void Relay::StartLiveWindowMode() {
297 if (m_table != nullptr) {
298 m_table->AddTableListener("Value", this, true);
299 }
300}
301
302void Relay::StopLiveWindowMode() {
303 if (m_table != nullptr) {
304 m_table->RemoveTableListener(this);
305 }
306}
307
308std::string Relay::GetSmartDashboardType() const { return "Relay"; }
309
310void Relay::InitTable(std::shared_ptr<ITable> subTable) {
311 m_table = subTable;
312 UpdateTable();
313}
314
315std::shared_ptr<ITable> Relay::GetTable() const { return m_table; }