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