blob: 0f32e1e7294ccf7f1df0f984d0c175ccc29bda35 [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
Austin Schuh75263e32022-02-22 18:05:32 -08007#include <fmt/format.h>
Austin Schuh812d0d12021-11-04 20:16:48 -07008#include <hal/REVPH.h>
9#include <wpi/NullDeleter.h>
10#include <wpi/StackTrace.h>
11
12#include "frc/Compressor.h"
13#include "frc/DoubleSolenoid.h"
14#include "frc/Errors.h"
Austin Schuh75263e32022-02-22 18:05:32 -080015#include "frc/RobotBase.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070016#include "frc/SensorUtil.h"
17#include "frc/Solenoid.h"
Austin Schuh75263e32022-02-22 18:05:32 -080018#include "frc/fmt/Units.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070019
20using namespace frc;
21
Austin Schuh75263e32022-02-22 18:05:32 -080022/** Converts volts to PSI per the REV Analog Pressure Sensor datasheet. */
23units::pounds_per_square_inch_t VoltsToPSI(units::volt_t sensorVoltage,
24 units::volt_t supplyVoltage) {
25 auto pressure = 250 * (sensorVoltage.value() / supplyVoltage.value()) - 25;
26 return units::pounds_per_square_inch_t{pressure};
27}
28
29/** Converts PSI to volts per the REV Analog Pressure Sensor datasheet. */
30units::volt_t PSIToVolts(units::pounds_per_square_inch_t pressure,
31 units::volt_t supplyVoltage) {
32 auto voltage = supplyVoltage.value() * (0.004 * pressure.value() + 0.1);
33 return units::volt_t{voltage};
34}
35
Austin Schuh812d0d12021-11-04 20:16:48 -070036wpi::mutex PneumaticHub::m_handleLock;
37std::unique_ptr<wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>
38 PneumaticHub::m_handleMap = nullptr;
39
40// Always called under lock, so we can avoid the double lock from the magic
41// static
42std::weak_ptr<PneumaticHub::DataStore>& PneumaticHub::GetDataStore(int module) {
43 if (!m_handleMap) {
44 m_handleMap = std::make_unique<
45 wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>();
46 }
47 return (*m_handleMap)[module];
48}
49
50class PneumaticHub::DataStore {
51 public:
52 explicit DataStore(int module, const char* stackTrace) {
53 int32_t status = 0;
54 HAL_REVPHHandle handle = HAL_InitializeREVPH(module, stackTrace, &status);
55 FRC_CheckErrorStatus(status, "Module {}", module);
56 m_moduleObject = PneumaticHub{handle, module};
57 m_moduleObject.m_dataStore =
58 std::shared_ptr<DataStore>{this, wpi::NullDeleter<DataStore>()};
Austin Schuh75263e32022-02-22 18:05:32 -080059
60 auto version = m_moduleObject.GetVersion();
61
62 if (version.FirmwareMajor > 0 && RobotBase::IsReal()) {
63 // Write PH firmware version to roboRIO
64 std::FILE* file = nullptr;
65 file = std::fopen(
66 fmt::format("/tmp/frc_versions/REV_PH_{:0>2}_WPILib_Version.ini",
67 module)
68 .c_str(),
69 "w");
70 if (file != nullptr) {
71 std::fputs("[Version]\n", file);
72 std::fputs(fmt::format("model=REV PH\n").c_str(), file);
73 std::fputs(fmt::format("deviceID={:x}\n", (0x9052600 | module)).c_str(),
74 file);
75 std::fputs(fmt::format("currentVersion={}.{}.{}", version.FirmwareMajor,
76 version.FirmwareMinor, version.FirmwareFix)
77 .c_str(),
78 file);
79 std::fclose(file);
80 }
81 }
82
83 // Check PH firmware version
84 if (version.FirmwareMajor > 0 && version.FirmwareMajor < 22) {
85 throw FRC_MakeError(
86 err::AssertionFailure,
87 "The Pneumatic Hub has firmware version {}.{}.{}, and must be "
88 "updated to version 2022.0.0 or later using the REV Hardware Client",
89 version.FirmwareMajor, version.FirmwareMinor, version.FirmwareFix);
90 }
Austin Schuh812d0d12021-11-04 20:16:48 -070091 }
92
93 ~DataStore() noexcept { HAL_FreeREVPH(m_moduleObject.m_handle); }
94
95 DataStore(DataStore&&) = delete;
96 DataStore& operator=(DataStore&&) = delete;
97
98 private:
99 friend class PneumaticHub;
100 uint32_t m_reservedMask{0};
101 bool m_compressorReserved{false};
102 wpi::mutex m_reservedLock;
103 PneumaticHub m_moduleObject{HAL_kInvalidHandle, 0};
Austin Schuh75263e32022-02-22 18:05:32 -0800104 std::array<units::millisecond_t, 16> m_oneShotDurMs{0_ms};
Austin Schuh812d0d12021-11-04 20:16:48 -0700105};
106
107PneumaticHub::PneumaticHub()
108 : PneumaticHub{SensorUtil::GetDefaultREVPHModule()} {}
109
110PneumaticHub::PneumaticHub(int module) {
111 std::string stackTrace = wpi::GetStackTrace(1);
112 std::scoped_lock lock(m_handleLock);
113 auto& res = GetDataStore(module);
114 m_dataStore = res.lock();
115 if (!m_dataStore) {
116 m_dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
117 res = m_dataStore;
118 }
119 m_handle = m_dataStore->m_moduleObject.m_handle;
120 m_module = module;
121}
122
123PneumaticHub::PneumaticHub(HAL_REVPHHandle handle, int module)
124 : m_handle{handle}, m_module{module} {}
125
126bool PneumaticHub::GetCompressor() const {
127 int32_t status = 0;
128 auto result = HAL_GetREVPHCompressor(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800129 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700130 return result;
131}
132
Austin Schuh75263e32022-02-22 18:05:32 -0800133void PneumaticHub::DisableCompressor() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700134 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800135 HAL_SetREVPHClosedLoopControlDisabled(m_handle, &status);
136 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700137}
138
Austin Schuh75263e32022-02-22 18:05:32 -0800139void PneumaticHub::EnableCompressorDigital() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700140 int32_t status = 0;
Austin Schuh75263e32022-02-22 18:05:32 -0800141 HAL_SetREVPHClosedLoopControlDigital(m_handle, &status);
142 FRC_ReportError(status, "Module {}", m_module);
143}
144
145void PneumaticHub::EnableCompressorAnalog(
146 units::pounds_per_square_inch_t minPressure,
147 units::pounds_per_square_inch_t maxPressure) {
148 if (minPressure >= maxPressure) {
149 throw FRC_MakeError(err::InvalidParameter, "{}",
150 "maxPressure must be greater than minPresure");
151 }
152 if (minPressure < 0_psi || minPressure > 120_psi) {
153 throw FRC_MakeError(err::ParameterOutOfRange,
154 "minPressure must be between 0 and 120 PSI, got {}",
155 minPressure);
156 }
157 if (maxPressure < 0_psi || maxPressure > 120_psi) {
158 throw FRC_MakeError(err::ParameterOutOfRange,
159 "maxPressure must be between 0 and 120 PSI, got {}",
160 maxPressure);
161 }
162 int32_t status = 0;
163 units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
164 units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
165 HAL_SetREVPHClosedLoopControlAnalog(m_handle, minAnalogVoltage.value(),
166 maxAnalogVoltage.value(), &status);
167 FRC_ReportError(status, "Module {}", m_module);
168}
169
170void PneumaticHub::EnableCompressorHybrid(
171 units::pounds_per_square_inch_t minPressure,
172 units::pounds_per_square_inch_t maxPressure) {
173 if (minPressure >= maxPressure) {
174 throw FRC_MakeError(err::InvalidParameter, "{}",
175 "maxPressure must be greater than minPresure");
176 }
177 if (minPressure < 0_psi || minPressure > 120_psi) {
178 throw FRC_MakeError(err::ParameterOutOfRange,
179 "minPressure must be between 0 and 120 PSI, got {}",
180 minPressure);
181 }
182 if (maxPressure < 0_psi || maxPressure > 120_psi) {
183 throw FRC_MakeError(err::ParameterOutOfRange,
184 "maxPressure must be between 0 and 120 PSI, got {}",
185 maxPressure);
186 }
187 int32_t status = 0;
188 units::volt_t minAnalogVoltage = PSIToVolts(minPressure, 5_V);
189 units::volt_t maxAnalogVoltage = PSIToVolts(maxPressure, 5_V);
190 HAL_SetREVPHClosedLoopControlHybrid(m_handle, minAnalogVoltage.value(),
191 maxAnalogVoltage.value(), &status);
192 FRC_ReportError(status, "Module {}", m_module);
193}
194
195CompressorConfigType PneumaticHub::GetCompressorConfigType() const {
196 int32_t status = 0;
197 auto result = HAL_GetREVPHCompressorConfig(m_handle, &status);
198 FRC_ReportError(status, "Module {}", m_module);
199 return static_cast<CompressorConfigType>(result);
Austin Schuh812d0d12021-11-04 20:16:48 -0700200}
201
202bool PneumaticHub::GetPressureSwitch() const {
203 int32_t status = 0;
204 auto result = HAL_GetREVPHPressureSwitch(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800205 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700206 return result;
207}
208
Austin Schuh75263e32022-02-22 18:05:32 -0800209units::ampere_t PneumaticHub::GetCompressorCurrent() const {
Austin Schuh812d0d12021-11-04 20:16:48 -0700210 int32_t status = 0;
211 auto result = HAL_GetREVPHCompressorCurrent(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800212 FRC_ReportError(status, "Module {}", m_module);
213 return units::ampere_t{result};
Austin Schuh812d0d12021-11-04 20:16:48 -0700214}
215
216void PneumaticHub::SetSolenoids(int mask, int values) {
217 int32_t status = 0;
218 HAL_SetREVPHSolenoids(m_handle, mask, values, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800219 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700220}
221
222int PneumaticHub::GetSolenoids() const {
223 int32_t status = 0;
224 auto result = HAL_GetREVPHSolenoids(m_handle, &status);
Austin Schuh75263e32022-02-22 18:05:32 -0800225 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700226 return result;
227}
228
229int PneumaticHub::GetModuleNumber() const {
230 return m_module;
231}
232
233int PneumaticHub::GetSolenoidDisabledList() const {
Austin Schuh75263e32022-02-22 18:05:32 -0800234 int32_t status = 0;
235 HAL_REVPHStickyFaults faults;
236 std::memset(&faults, 0, sizeof(faults));
237 HAL_GetREVPHStickyFaults(m_handle, &faults, &status);
238 FRC_ReportError(status, "Module {}", m_module);
239 uint32_t intFaults = 0;
240 static_assert(sizeof(faults) == sizeof(intFaults));
241 std::memcpy(&intFaults, &faults, sizeof(faults));
242 return intFaults & 0xFFFF;
Austin Schuh812d0d12021-11-04 20:16:48 -0700243}
244
245void PneumaticHub::FireOneShot(int index) {
Austin Schuh75263e32022-02-22 18:05:32 -0800246 int32_t status = 0;
247 HAL_FireREVPHOneShot(m_handle, index,
248 m_dataStore->m_oneShotDurMs[index].value(), &status);
249 FRC_ReportError(status, "Module {}", m_module);
Austin Schuh812d0d12021-11-04 20:16:48 -0700250}
251
252void PneumaticHub::SetOneShotDuration(int index, units::second_t duration) {
Austin Schuh75263e32022-02-22 18:05:32 -0800253 m_dataStore->m_oneShotDurMs[index] = duration;
Austin Schuh812d0d12021-11-04 20:16:48 -0700254}
255
256bool PneumaticHub::CheckSolenoidChannel(int channel) const {
257 return HAL_CheckREVPHSolenoidChannel(channel);
258}
259
260int PneumaticHub::CheckAndReserveSolenoids(int mask) {
261 std::scoped_lock lock{m_dataStore->m_reservedLock};
262 uint32_t uMask = static_cast<uint32_t>(mask);
263 if ((m_dataStore->m_reservedMask & uMask) != 0) {
264 return m_dataStore->m_reservedMask & uMask;
265 }
266 m_dataStore->m_reservedMask |= uMask;
267 return 0;
268}
269
270void PneumaticHub::UnreserveSolenoids(int mask) {
271 std::scoped_lock lock{m_dataStore->m_reservedLock};
272 m_dataStore->m_reservedMask &= ~(static_cast<uint32_t>(mask));
273}
274
275bool PneumaticHub::ReserveCompressor() {
276 std::scoped_lock lock{m_dataStore->m_reservedLock};
277 if (m_dataStore->m_compressorReserved) {
278 return false;
279 }
280 m_dataStore->m_compressorReserved = true;
281 return true;
282}
283
284void PneumaticHub::UnreserveCompressor() {
285 std::scoped_lock lock{m_dataStore->m_reservedLock};
286 m_dataStore->m_compressorReserved = false;
287}
288
Austin Schuh75263e32022-02-22 18:05:32 -0800289PneumaticHub::Version PneumaticHub::GetVersion() const {
290 int32_t status = 0;
291 HAL_REVPHVersion halVersions;
292 std::memset(&halVersions, 0, sizeof(halVersions));
293 HAL_GetREVPHVersion(m_handle, &halVersions, &status);
294 FRC_ReportError(status, "Module {}", m_module);
295 PneumaticHub::Version versions;
296 static_assert(sizeof(halVersions) == sizeof(versions));
297 static_assert(std::is_standard_layout_v<decltype(versions)>);
298 static_assert(std::is_trivial_v<decltype(versions)>);
299 std::memcpy(&versions, &halVersions, sizeof(versions));
300 return versions;
301}
302
303PneumaticHub::Faults PneumaticHub::GetFaults() const {
304 int32_t status = 0;
305 HAL_REVPHFaults halFaults;
306 std::memset(&halFaults, 0, sizeof(halFaults));
307 HAL_GetREVPHFaults(m_handle, &halFaults, &status);
308 FRC_ReportError(status, "Module {}", m_module);
309 PneumaticHub::Faults faults;
310 static_assert(sizeof(halFaults) == sizeof(faults));
311 static_assert(std::is_standard_layout_v<decltype(faults)>);
312 static_assert(std::is_trivial_v<decltype(faults)>);
313 std::memcpy(&faults, &halFaults, sizeof(faults));
314 return faults;
315}
316
317PneumaticHub::StickyFaults PneumaticHub::GetStickyFaults() const {
318 int32_t status = 0;
319 HAL_REVPHStickyFaults halStickyFaults;
320 std::memset(&halStickyFaults, 0, sizeof(halStickyFaults));
321 HAL_GetREVPHStickyFaults(m_handle, &halStickyFaults, &status);
322 FRC_ReportError(status, "Module {}", m_module);
323 PneumaticHub::StickyFaults stickyFaults;
324 static_assert(sizeof(halStickyFaults) == sizeof(stickyFaults));
325 static_assert(std::is_standard_layout_v<decltype(stickyFaults)>);
326 static_assert(std::is_trivial_v<decltype(stickyFaults)>);
327 std::memcpy(&stickyFaults, &halStickyFaults, sizeof(stickyFaults));
328 return stickyFaults;
329}
330
331void PneumaticHub::ClearStickyFaults() {
332 int32_t status = 0;
333 HAL_ClearREVPHStickyFaults(m_handle, &status);
334 FRC_ReportError(status, "Module {}", m_module);
335}
336
337units::volt_t PneumaticHub::GetInputVoltage() const {
338 int32_t status = 0;
339 auto voltage = HAL_GetREVPHVoltage(m_handle, &status);
340 FRC_ReportError(status, "Module {}", m_module);
341 return units::volt_t{voltage};
342}
343
344units::volt_t PneumaticHub::Get5VRegulatedVoltage() const {
345 int32_t status = 0;
346 auto voltage = HAL_GetREVPH5VVoltage(m_handle, &status);
347 FRC_ReportError(status, "Module {}", m_module);
348 return units::volt_t{voltage};
349}
350
351units::ampere_t PneumaticHub::GetSolenoidsTotalCurrent() const {
352 int32_t status = 0;
353 auto current = HAL_GetREVPHSolenoidCurrent(m_handle, &status);
354 FRC_ReportError(status, "Module {}", m_module);
355 return units::ampere_t{current};
356}
357
358units::volt_t PneumaticHub::GetSolenoidsVoltage() const {
359 int32_t status = 0;
360 auto voltage = HAL_GetREVPHSolenoidVoltage(m_handle, &status);
361 FRC_ReportError(status, "Module {}", m_module);
362 return units::volt_t{voltage};
363}
364
365units::volt_t PneumaticHub::GetAnalogVoltage(int channel) const {
366 int32_t status = 0;
367 auto voltage = HAL_GetREVPHAnalogVoltage(m_handle, channel, &status);
368 FRC_ReportError(status, "Module {}", m_module);
369 return units::volt_t{voltage};
370}
371
372units::pounds_per_square_inch_t PneumaticHub::GetPressure(int channel) const {
373 int32_t status = 0;
374 auto sensorVoltage = HAL_GetREVPHAnalogVoltage(m_handle, channel, &status);
375 FRC_ReportError(status, "Module {}", m_module);
376 auto supplyVoltage = HAL_GetREVPH5VVoltage(m_handle, &status);
377 FRC_ReportError(status, "Module {}", m_module);
378 return VoltsToPSI(units::volt_t{sensorVoltage}, units::volt_t{supplyVoltage});
379}
380
Austin Schuh812d0d12021-11-04 20:16:48 -0700381Solenoid PneumaticHub::MakeSolenoid(int channel) {
382 return Solenoid{m_module, PneumaticsModuleType::REVPH, channel};
383}
384
385DoubleSolenoid PneumaticHub::MakeDoubleSolenoid(int forwardChannel,
386 int reverseChannel) {
387 return DoubleSolenoid{m_module, PneumaticsModuleType::REVPH, forwardChannel,
388 reverseChannel};
389}
390
391Compressor PneumaticHub::MakeCompressor() {
392 return Compressor{m_module, PneumaticsModuleType::REVPH};
393}
394
395std::shared_ptr<PneumaticsBase> PneumaticHub::GetForModule(int module) {
396 std::string stackTrace = wpi::GetStackTrace(1);
397 std::scoped_lock lock(m_handleLock);
398 auto& res = GetDataStore(module);
399 std::shared_ptr<DataStore> dataStore = res.lock();
400 if (!dataStore) {
401 dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
402 res = dataStore;
403 }
404
405 return std::shared_ptr<PneumaticsBase>{dataStore, &dataStore->m_moduleObject};
406}