blob: bcbbd55802644a283814517c4962cd101f4adba4 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "frc/PneumaticHub.h"
6
James Kuszmaulcf324122023-01-14 14:07:17 -08007#include <array>
8
Austin Schuh75263e32022-02-22 18:05:32 -08009#include <fmt/format.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070010#include <hal/REVPH.h>
11#include <wpi/NullDeleter.h>
12#include <wpi/StackTrace.h>
13
14#include "frc/Compressor.h"
15#include "frc/DoubleSolenoid.h"
16#include "frc/Errors.h"
Austin Schuh75263e32022-02-22 18:05:32 -080017#include "frc/RobotBase.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070018#include "frc/SensorUtil.h"
19#include "frc/Solenoid.h"
20
21using namespace frc;
22
Austin Schuh75263e32022-02-22 18:05:32 -080023/** Converts volts to PSI per the REV Analog Pressure Sensor datasheet. */
24units::pounds_per_square_inch_t VoltsToPSI(units::volt_t sensorVoltage,
25 units::volt_t supplyVoltage) {
26 auto pressure = 250 * (sensorVoltage.value() / supplyVoltage.value()) - 25;
27 return units::pounds_per_square_inch_t{pressure};
28}
29
30/** Converts PSI to volts per the REV Analog Pressure Sensor datasheet. */
31units::volt_t PSIToVolts(units::pounds_per_square_inch_t pressure,
32 units::volt_t supplyVoltage) {
33 auto voltage = supplyVoltage.value() * (0.004 * pressure.value() + 0.1);
34 return units::volt_t{voltage};
35}
36
Austin Schuh812d0d12021-11-04 20:16:48 -070037wpi::mutex PneumaticHub::m_handleLock;
38std::unique_ptr<wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>
39 PneumaticHub::m_handleMap = nullptr;
40
41// Always called under lock, so we can avoid the double lock from the magic
42// static
43std::weak_ptr<PneumaticHub::DataStore>& PneumaticHub::GetDataStore(int module) {
44 if (!m_handleMap) {
45 m_handleMap = std::make_unique<
46 wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>();
47 }
48 return (*m_handleMap)[module];
49}
50
51class PneumaticHub::DataStore {
52 public:
53 explicit DataStore(int module, const char* stackTrace) {
54 int32_t status = 0;
55 HAL_REVPHHandle handle = HAL_InitializeREVPH(module, stackTrace, &status);
56 FRC_CheckErrorStatus(status, "Module {}", module);
57 m_moduleObject = PneumaticHub{handle, module};
58 m_moduleObject.m_dataStore =
59 std::shared_ptr<DataStore>{this, wpi::NullDeleter<DataStore>()};
Austin Schuh75263e32022-02-22 18:05:32 -080060
61 auto version = m_moduleObject.GetVersion();
62
63 if (version.FirmwareMajor > 0 && RobotBase::IsReal()) {
64 // Write PH firmware version to roboRIO
65 std::FILE* file = nullptr;
66 file = std::fopen(
67 fmt::format("/tmp/frc_versions/REV_PH_{:0>2}_WPILib_Version.ini",
68 module)
69 .c_str(),
70 "w");
71 if (file != nullptr) {
72 std::fputs("[Version]\n", file);
73 std::fputs(fmt::format("model=REV PH\n").c_str(), file);
74 std::fputs(fmt::format("deviceID={:x}\n", (0x9052600 | module)).c_str(),
75 file);
76 std::fputs(fmt::format("currentVersion={}.{}.{}", version.FirmwareMajor,
77 version.FirmwareMinor, version.FirmwareFix)
78 .c_str(),
79 file);
80 std::fclose(file);
81 }
82 }
83
84 // Check PH firmware version
85 if (version.FirmwareMajor > 0 && version.FirmwareMajor < 22) {
86 throw FRC_MakeError(
87 err::AssertionFailure,
88 "The Pneumatic Hub has firmware version {}.{}.{}, and must be "
89 "updated to version 2022.0.0 or later using the REV Hardware Client",
90 version.FirmwareMajor, version.FirmwareMinor, version.FirmwareFix);
91 }
Austin Schuh812d0d12021-11-04 20:16:48 -070092 }
93
94 ~DataStore() noexcept { HAL_FreeREVPH(m_moduleObject.m_handle); }
95
96 DataStore(DataStore&&) = delete;
97 DataStore& operator=(DataStore&&) = delete;
98
99 private:
100 friend class PneumaticHub;
101 uint32_t m_reservedMask{0};
102 bool m_compressorReserved{false};
103 wpi::mutex m_reservedLock;
104 PneumaticHub m_moduleObject{HAL_kInvalidHandle, 0};
Austin Schuh75263e32022-02-22 18:05:32 -0800105 std::array<units::millisecond_t, 16> m_oneShotDurMs{0_ms};
Austin Schuh812d0d12021-11-04 20:16:48 -0700106};
107
108PneumaticHub::PneumaticHub()
109 : PneumaticHub{SensorUtil::GetDefaultREVPHModule()} {}
110
111PneumaticHub::PneumaticHub(int module) {
112 std::string stackTrace = wpi::GetStackTrace(1);
113 std::scoped_lock lock(m_handleLock);
114 auto& res = GetDataStore(module);
115 m_dataStore = res.lock();
116 if (!m_dataStore) {
117 m_dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
118 res = m_dataStore;
119 }
120 m_handle = m_dataStore->m_moduleObject.m_handle;
121 m_module = module;
122}
123
124PneumaticHub::PneumaticHub(HAL_REVPHHandle handle, int module)
125 : m_handle{handle}, m_module{module} {}
126
127bool PneumaticHub::GetCompressor() const {
128 int32_t status = 0;
129 auto result = HAL_GetREVPHCompressor(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800130 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700131 return result;
132}
133
Austin Schuh75263e32022-02-22 18:05:32 -0800134void PneumaticHub::DisableCompressor() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800136 HAL_SetREVPHClosedLoopControlDisabled(m_handle, &status);
137 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700138}
139
Austin Schuh75263e32022-02-22 18:05:32 -0800140void PneumaticHub::EnableCompressorDigital() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700141 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800142 HAL_SetREVPHClosedLoopControlDigital(m_handle, &status);
143 FRC_ReportError(status, "Module {}", m_module);
144}
145
146void PneumaticHub::EnableCompressorAnalog(
147 units::pounds_per_square_inch_t minPressure,
148 units::pounds_per_square_inch_t maxPressure) {
149 if (minPressure >= maxPressure) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800150 throw FRC_MakeError(err::InvalidParameter,
Austin Schuh75263e32022-02-22 18:05:32 -0800151 "maxPressure must be greater than minPresure");
152 }
153 if (minPressure < 0_psi || minPressure > 120_psi) {
154 throw FRC_MakeError(err::ParameterOutOfRange,
155 "minPressure must be between 0 and 120 PSI, got {}",
156 minPressure);
157 }
158 if (maxPressure < 0_psi || maxPressure > 120_psi) {
159 throw FRC_MakeError(err::ParameterOutOfRange,
160 "maxPressure must be between 0 and 120 PSI, got {}",
161 maxPressure);
162 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800163
164 // Send the voltage as it would be if the 5V rail was at exactly 5V.
165 // The firmware will compensate for the real 5V rail voltage, which
166 // can fluctuate somewhat over time.
Austin Schuh75263e32022-02-22 18:05:32 -0800167 units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
168 units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800169
170 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800171 HAL_SetREVPHClosedLoopControlAnalog(m_handle, minAnalogVoltage.value(),
172 maxAnalogVoltage.value(), &status);
173 FRC_ReportError(status, "Module {}", m_module);
174}
175
176void PneumaticHub::EnableCompressorHybrid(
177 units::pounds_per_square_inch_t minPressure,
178 units::pounds_per_square_inch_t maxPressure) {
179 if (minPressure >= maxPressure) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800180 throw FRC_MakeError(err::InvalidParameter,
Austin Schuh75263e32022-02-22 18:05:32 -0800181 "maxPressure must be greater than minPresure");
182 }
183 if (minPressure < 0_psi || minPressure > 120_psi) {
184 throw FRC_MakeError(err::ParameterOutOfRange,
185 "minPressure must be between 0 and 120 PSI, got {}",
186 minPressure);
187 }
188 if (maxPressure < 0_psi || maxPressure > 120_psi) {
189 throw FRC_MakeError(err::ParameterOutOfRange,
190 "maxPressure must be between 0 and 120 PSI, got {}",
191 maxPressure);
192 }
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800193
194 // Send the voltage as it would be if the 5V rail was at exactly 5V.
195 // The firmware will compensate for the real 5V rail voltage, which
196 // can fluctuate somewhat over time.
Austin Schuh75263e32022-02-22 18:05:32 -0800197 units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
198 units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800199
200 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800201 HAL_SetREVPHClosedLoopControlHybrid(m_handle, minAnalogVoltage.value(),
202 maxAnalogVoltage.value(), &status);
203 FRC_ReportError(status, "Module {}", m_module);
204}
205
206CompressorConfigType PneumaticHub::GetCompressorConfigType() const {
207 int32_t status = 0;
208 auto result = HAL_GetREVPHCompressorConfig(m_handle, &status);
209 FRC_ReportError(status, "Module {}", m_module);
210 return static_cast<CompressorConfigType>(result);
Austin Schuh812d0d12021-11-04 20:16:48 -0700211}
212
213bool PneumaticHub::GetPressureSwitch() const {
214 int32_t status = 0;
215 auto result = HAL_GetREVPHPressureSwitch(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800216 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700217 return result;
218}
219
Austin Schuh75263e32022-02-22 18:05:32 -0800220units::ampere_t PneumaticHub::GetCompressorCurrent() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700221 int32_t status = 0;
222 auto result = HAL_GetREVPHCompressorCurrent(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800223 FRC_ReportError(status, "Module {}", m_module);
224 return units::ampere_t{result};
Austin Schuh812d0d12021-11-04 20:16:48 -0700225}
226
227void PneumaticHub::SetSolenoids(int mask, int values) {
228 int32_t status = 0;
229 HAL_SetREVPHSolenoids(m_handle, mask, values, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800230 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700231}
232
233int PneumaticHub::GetSolenoids() const {
234 int32_t status = 0;
235 auto result = HAL_GetREVPHSolenoids(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800236 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700237 return result;
238}
239
240int PneumaticHub::GetModuleNumber() const {
241 return m_module;
242}
243
244int PneumaticHub::GetSolenoidDisabledList() const {
Austin Schuh75263e32022-02-22 18:05:32 -0800245 int32_t status = 0;
246 HAL_REVPHStickyFaults faults;
247 std::memset(&faults, 0, sizeof(faults));
248 HAL_GetREVPHStickyFaults(m_handle, &faults, &status);
249 FRC_ReportError(status, "Module {}", m_module);
250 uint32_t intFaults = 0;
251 static_assert(sizeof(faults) == sizeof(intFaults));
252 std::memcpy(&intFaults, &faults, sizeof(faults));
253 return intFaults & 0xFFFF;
Austin Schuh812d0d12021-11-04 20:16:48 -0700254}
255
256void PneumaticHub::FireOneShot(int index) {
Austin Schuh75263e32022-02-22 18:05:32 -0800257 int32_t status = 0;
258 HAL_FireREVPHOneShot(m_handle, index,
259 m_dataStore->m_oneShotDurMs[index].value(), &status);
260 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700261}
262
263void PneumaticHub::SetOneShotDuration(int index, units::second_t duration) {
Austin Schuh75263e32022-02-22 18:05:32 -0800264 m_dataStore->m_oneShotDurMs[index] = duration;
Austin Schuh812d0d12021-11-04 20:16:48 -0700265}
266
267bool PneumaticHub::CheckSolenoidChannel(int channel) const {
268 return HAL_CheckREVPHSolenoidChannel(channel);
269}
270
271int PneumaticHub::CheckAndReserveSolenoids(int mask) {
272 std::scoped_lock lock{m_dataStore->m_reservedLock};
273 uint32_t uMask = static_cast<uint32_t>(mask);
274 if ((m_dataStore->m_reservedMask & uMask) != 0) {
275 return m_dataStore->m_reservedMask & uMask;
276 }
277 m_dataStore->m_reservedMask |= uMask;
278 return 0;
279}
280
281void PneumaticHub::UnreserveSolenoids(int mask) {
282 std::scoped_lock lock{m_dataStore->m_reservedLock};
283 m_dataStore->m_reservedMask &= ~(static_cast<uint32_t>(mask));
284}
285
286bool PneumaticHub::ReserveCompressor() {
287 std::scoped_lock lock{m_dataStore->m_reservedLock};
288 if (m_dataStore->m_compressorReserved) {
289 return false;
290 }
291 m_dataStore->m_compressorReserved = true;
292 return true;
293}
294
295void PneumaticHub::UnreserveCompressor() {
296 std::scoped_lock lock{m_dataStore->m_reservedLock};
297 m_dataStore->m_compressorReserved = false;
298}
299
Austin Schuh75263e32022-02-22 18:05:32 -0800300PneumaticHub::Version PneumaticHub::GetVersion() const {
301 int32_t status = 0;
302 HAL_REVPHVersion halVersions;
303 std::memset(&halVersions, 0, sizeof(halVersions));
304 HAL_GetREVPHVersion(m_handle, &halVersions, &status);
305 FRC_ReportError(status, "Module {}", m_module);
306 PneumaticHub::Version versions;
307 static_assert(sizeof(halVersions) == sizeof(versions));
308 static_assert(std::is_standard_layout_v<decltype(versions)>);
309 static_assert(std::is_trivial_v<decltype(versions)>);
310 std::memcpy(&versions, &halVersions, sizeof(versions));
311 return versions;
312}
313
314PneumaticHub::Faults PneumaticHub::GetFaults() const {
315 int32_t status = 0;
316 HAL_REVPHFaults halFaults;
317 std::memset(&halFaults, 0, sizeof(halFaults));
318 HAL_GetREVPHFaults(m_handle, &halFaults, &status);
319 FRC_ReportError(status, "Module {}", m_module);
320 PneumaticHub::Faults faults;
321 static_assert(sizeof(halFaults) == sizeof(faults));
322 static_assert(std::is_standard_layout_v<decltype(faults)>);
323 static_assert(std::is_trivial_v<decltype(faults)>);
324 std::memcpy(&faults, &halFaults, sizeof(faults));
325 return faults;
326}
327
328PneumaticHub::StickyFaults PneumaticHub::GetStickyFaults() const {
329 int32_t status = 0;
330 HAL_REVPHStickyFaults halStickyFaults;
331 std::memset(&halStickyFaults, 0, sizeof(halStickyFaults));
332 HAL_GetREVPHStickyFaults(m_handle, &halStickyFaults, &status);
333 FRC_ReportError(status, "Module {}", m_module);
334 PneumaticHub::StickyFaults stickyFaults;
335 static_assert(sizeof(halStickyFaults) == sizeof(stickyFaults));
336 static_assert(std::is_standard_layout_v<decltype(stickyFaults)>);
337 static_assert(std::is_trivial_v<decltype(stickyFaults)>);
338 std::memcpy(&stickyFaults, &halStickyFaults, sizeof(stickyFaults));
339 return stickyFaults;
340}
341
342void PneumaticHub::ClearStickyFaults() {
343 int32_t status = 0;
344 HAL_ClearREVPHStickyFaults(m_handle, &status);
345 FRC_ReportError(status, "Module {}", m_module);
346}
347
348units::volt_t PneumaticHub::GetInputVoltage() const {
349 int32_t status = 0;
350 auto voltage = HAL_GetREVPHVoltage(m_handle, &status);
351 FRC_ReportError(status, "Module {}", m_module);
352 return units::volt_t{voltage};
353}
354
355units::volt_t PneumaticHub::Get5VRegulatedVoltage() const {
356 int32_t status = 0;
357 auto voltage = HAL_GetREVPH5VVoltage(m_handle, &status);
358 FRC_ReportError(status, "Module {}", m_module);
359 return units::volt_t{voltage};
360}
361
362units::ampere_t PneumaticHub::GetSolenoidsTotalCurrent() const {
363 int32_t status = 0;
364 auto current = HAL_GetREVPHSolenoidCurrent(m_handle, &status);
365 FRC_ReportError(status, "Module {}", m_module);
366 return units::ampere_t{current};
367}
368
369units::volt_t PneumaticHub::GetSolenoidsVoltage() const {
370 int32_t status = 0;
371 auto voltage = HAL_GetREVPHSolenoidVoltage(m_handle, &status);
372 FRC_ReportError(status, "Module {}", m_module);
373 return units::volt_t{voltage};
374}
375
376units::volt_t PneumaticHub::GetAnalogVoltage(int channel) const {
377 int32_t status = 0;
378 auto voltage = HAL_GetREVPHAnalogVoltage(m_handle, channel, &status);
379 FRC_ReportError(status, "Module {}", m_module);
380 return units::volt_t{voltage};
381}
382
383units::pounds_per_square_inch_t PneumaticHub::GetPressure(int channel) const {
384 int32_t status = 0;
385 auto sensorVoltage = HAL_GetREVPHAnalogVoltage(m_handle, channel, &status);
386 FRC_ReportError(status, "Module {}", m_module);
387 auto supplyVoltage = HAL_GetREVPH5VVoltage(m_handle, &status);
388 FRC_ReportError(status, "Module {}", m_module);
389 return VoltsToPSI(units::volt_t{sensorVoltage}, units::volt_t{supplyVoltage});
390}
391
Austin Schuh812d0d12021-11-04 20:16:48 -0700392Solenoid PneumaticHub::MakeSolenoid(int channel) {
393 return Solenoid{m_module, PneumaticsModuleType::REVPH, channel};
394}
395
396DoubleSolenoid PneumaticHub::MakeDoubleSolenoid(int forwardChannel,
397 int reverseChannel) {
398 return DoubleSolenoid{m_module, PneumaticsModuleType::REVPH, forwardChannel,
399 reverseChannel};
400}
401
402Compressor PneumaticHub::MakeCompressor() {
403 return Compressor{m_module, PneumaticsModuleType::REVPH};
404}
405
406std::shared_ptr<PneumaticsBase> PneumaticHub::GetForModule(int module) {
407 std::string stackTrace = wpi::GetStackTrace(1);
408 std::scoped_lock lock(m_handleLock);
409 auto& res = GetDataStore(module);
410 std::shared_ptr<DataStore> dataStore = res.lock();
411 if (!dataStore) {
412 dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
413 res = dataStore;
414 }
415
416 return std::shared_ptr<PneumaticsBase>{dataStore, &dataStore->m_moduleObject};
417}