blob: 155f92c1ff0ed12c01bf6d90f1b6a3f11c5c9d54 [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 "hal/REVPH.h"
6
7#include <fmt/format.h>
8
9#include "HALInitializer.h"
10#include "HALInternal.h"
11#include "PortsInternal.h"
12#include "hal/CANAPI.h"
13#include "hal/Errors.h"
14#include "hal/handles/IndexedHandleResource.h"
15#include "rev/PHFrames.h"
16
17using namespace hal;
18
19static constexpr HAL_CANManufacturer manufacturer =
20 HAL_CANManufacturer::HAL_CAN_Man_kREV;
21
22static constexpr HAL_CANDeviceType deviceType =
23 HAL_CANDeviceType::HAL_CAN_Dev_kPneumatics;
24
25static constexpr int32_t kDefaultControlPeriod = 20;
26// static constexpr uint8_t kDefaultSensorMask = (1 <<
27// HAL_REV_PHSENSOR_DIGITAL);
28static constexpr uint8_t kDefaultCompressorDuty = 255;
29static constexpr uint8_t kDefaultPressureTarget = 120;
30static constexpr uint8_t kDefaultPressureHysteresis = 60;
31
32#define HAL_REV_MAX_PULSE_TIME 65534
33#define HAL_REV_MAX_PRESSURE_TARGET 120
34#define HAL_REV_MAX_PRESSURE_HYSTERESIS HAL_REV_MAX_PRESSURE_TARGET
35
36static constexpr uint32_t APIFromExtId(uint32_t extId) {
37 return (extId >> 6) & 0x3FF;
38}
39
40static constexpr uint32_t PH_SET_ALL_FRAME_API =
41 APIFromExtId(PH_SET_ALL_FRAME_ID);
42static constexpr uint32_t PH_PULSE_ONCE_FRAME_API =
43 APIFromExtId(PH_PULSE_ONCE_FRAME_ID);
44static constexpr uint32_t PH_STATUS0_FRAME_API =
45 APIFromExtId(PH_STATUS0_FRAME_ID);
46static constexpr uint32_t PH_STATUS1_FRAME_API =
47 APIFromExtId(PH_STATUS1_FRAME_ID);
48
49static constexpr int32_t kPHFrameStatus0Timeout = 50;
50static constexpr int32_t kPHFrameStatus1Timeout = 50;
51
52namespace {
53
54struct REV_PHObj {
55 int32_t controlPeriod;
56 PH_set_all_t desiredSolenoidsState;
57 wpi::mutex solenoidLock;
58 HAL_CANHandle hcan;
59 std::string previousAllocation;
60};
61
62} // namespace
63
64static IndexedHandleResource<HAL_REVPHHandle, REV_PHObj, 63,
65 HAL_HandleEnum::REVPH>* REVPHHandles;
66
67namespace hal::init {
68void InitializeREVPH() {
69 static IndexedHandleResource<HAL_REVPHHandle, REV_PHObj, kNumREVPHModules,
70 HAL_HandleEnum::REVPH>
71 rH;
72 REVPHHandles = &rH;
73}
74} // namespace hal::init
75
76static PH_status0_t HAL_REV_ReadPHStatus0(HAL_CANHandle hcan, int32_t* status) {
77 uint8_t packedData[8] = {0};
78 int32_t length = 0;
79 uint64_t timestamp = 0;
80 PH_status0_t result = {};
81
82 HAL_ReadCANPacketTimeout(hcan, PH_STATUS0_FRAME_API, packedData, &length,
83 &timestamp, kPHFrameStatus0Timeout * 2, status);
84
85 if (*status != 0) {
86 return result;
87 }
88
89 PH_status0_unpack(&result, packedData, PH_STATUS0_LENGTH);
90
91 return result;
92}
93
94static PH_status1_t HAL_REV_ReadPHStatus1(HAL_CANHandle hcan, int32_t* status) {
95 uint8_t packedData[8] = {0};
96 int32_t length = 0;
97 uint64_t timestamp = 0;
98 PH_status1_t result = {};
99
100 HAL_ReadCANPacketTimeout(hcan, PH_STATUS1_FRAME_API, packedData, &length,
101 &timestamp, kPHFrameStatus1Timeout * 2, status);
102
103 if (*status != 0) {
104 return result;
105 }
106
107 PH_status1_unpack(&result, packedData, PH_STATUS1_LENGTH);
108
109 return result;
110}
111
112enum REV_SolenoidState {
113 kSolenoidDisabled = 0,
114 kSolenoidEnabled,
115 kSolenoidControlledViaPulse
116};
117
118static void HAL_REV_UpdateDesiredPHSolenoidState(REV_PHObj* hph,
119 int32_t solenoid,
120 REV_SolenoidState state) {
121 switch (solenoid) {
122 case 0:
123 hph->desiredSolenoidsState.channel_0 = state;
124 break;
125 case 1:
126 hph->desiredSolenoidsState.channel_1 = state;
127 break;
128 case 2:
129 hph->desiredSolenoidsState.channel_2 = state;
130 break;
131 case 3:
132 hph->desiredSolenoidsState.channel_3 = state;
133 break;
134 case 4:
135 hph->desiredSolenoidsState.channel_4 = state;
136 break;
137 case 5:
138 hph->desiredSolenoidsState.channel_5 = state;
139 break;
140 case 6:
141 hph->desiredSolenoidsState.channel_6 = state;
142 break;
143 case 7:
144 hph->desiredSolenoidsState.channel_7 = state;
145 break;
146 case 8:
147 hph->desiredSolenoidsState.channel_8 = state;
148 break;
149 case 9:
150 hph->desiredSolenoidsState.channel_9 = state;
151 break;
152 case 10:
153 hph->desiredSolenoidsState.channel_10 = state;
154 break;
155 case 11:
156 hph->desiredSolenoidsState.channel_11 = state;
157 break;
158 case 12:
159 hph->desiredSolenoidsState.channel_12 = state;
160 break;
161 case 13:
162 hph->desiredSolenoidsState.channel_13 = state;
163 break;
164 case 14:
165 hph->desiredSolenoidsState.channel_14 = state;
166 break;
167 case 15:
168 hph->desiredSolenoidsState.channel_15 = state;
169 break;
170 }
171}
172
173static void HAL_REV_SendSolenoidsState(REV_PHObj* hph, int32_t* status) {
174 uint8_t packedData[PH_SET_ALL_LENGTH] = {0};
175 PH_set_all_pack(packedData, &(hph->desiredSolenoidsState), PH_SET_ALL_LENGTH);
176 HAL_WriteCANPacketRepeating(hph->hcan, packedData, PH_SET_ALL_LENGTH,
177 PH_SET_ALL_FRAME_API, hph->controlPeriod, status);
178}
179
180static HAL_Bool HAL_REV_CheckPHPulseTime(int32_t time) {
181 return ((time > 0) && (time <= HAL_REV_MAX_PULSE_TIME)) ? 1 : 0;
182}
183
184HAL_REVPHHandle HAL_InitializeREVPH(int32_t module,
185 const char* allocationLocation,
186 int32_t* status) {
187 hal::init::CheckInit();
188 if (!HAL_CheckREVPHModuleNumber(module)) {
189 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
190 kNumREVPHModules, module);
191 return HAL_kInvalidHandle;
192 }
193
194 HAL_REVPHHandle handle;
195 auto hph = REVPHHandles->Allocate(module, &handle, status);
196 if (*status != 0) {
197 if (hph) {
198 hal::SetLastErrorPreviouslyAllocated(status, "REV PH", module,
199 hph->previousAllocation);
200 } else {
201 hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for REV PH", 1,
202 kNumREVPHModules, module);
203 }
204 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
205 }
206
207 HAL_CANHandle hcan =
208 HAL_InitializeCAN(manufacturer, module, deviceType, status);
209
210 if (*status != 0) {
211 REVPHHandles->Free(handle);
212 return HAL_kInvalidHandle;
213 }
214
215 hph->previousAllocation = allocationLocation ? allocationLocation : "";
216 hph->hcan = hcan;
217 hph->controlPeriod = kDefaultControlPeriod;
218
219 // Start closed-loop compressor control by starting solenoid state updates
220 HAL_REV_SendSolenoidsState(hph.get(), status);
221
222 return handle;
223}
224
225void HAL_FreeREVPH(HAL_REVPHHandle handle) {
226 auto hph = REVPHHandles->Get(handle);
227 if (hph == nullptr)
228 return;
229
230 HAL_CleanCAN(hph->hcan);
231
232 REVPHHandles->Free(handle);
233}
234
235HAL_Bool HAL_CheckREVPHModuleNumber(int32_t module) {
236 return module >= 1 && module < kNumREVPDHModules;
237}
238
239HAL_Bool HAL_CheckREVPHSolenoidChannel(int32_t channel) {
240 return channel >= 0 && channel < kNumREVPHChannels;
241}
242
243HAL_Bool HAL_GetREVPHCompressor(HAL_REVPHHandle handle, int32_t* status) {
244 auto ph = REVPHHandles->Get(handle);
245 if (ph == nullptr) {
246 *status = HAL_HANDLE_ERROR;
247 return false;
248 }
249
250 PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
251
252 if (*status != 0) {
253 return false;
254 }
255
256 return status0.compressor_on;
257}
258
259void HAL_SetREVPHClosedLoopControl(HAL_REVPHHandle handle, HAL_Bool enabled,
260 int32_t* status) {
261 // TODO
262}
263
264HAL_Bool HAL_GetREVPHClosedLoopControl(HAL_REVPHHandle handle,
265 int32_t* status) {
266 return false; // TODO
267}
268
269HAL_Bool HAL_GetREVPHPressureSwitch(HAL_REVPHHandle handle, int32_t* status) {
270 auto ph = REVPHHandles->Get(handle);
271 if (ph == nullptr) {
272 *status = HAL_HANDLE_ERROR;
273 return false;
274 }
275
276 PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
277
278 if (*status != 0) {
279 return false;
280 }
281
282 return status0.digital_sensor;
283}
284
285double HAL_GetREVPHCompressorCurrent(HAL_REVPHHandle handle, int32_t* status) {
286 auto ph = REVPHHandles->Get(handle);
287 if (ph == nullptr) {
288 *status = HAL_HANDLE_ERROR;
289 return 0;
290 }
291
292 PH_status1_t status1 = HAL_REV_ReadPHStatus1(ph->hcan, status);
293
294 if (*status != 0) {
295 return 0;
296 }
297
298 return PH_status1_compressor_current_decode(status1.compressor_current);
299}
300
301double HAL_GetREVPHAnalogPressure(HAL_REVPHHandle handle, int32_t channel,
302 int32_t* status) {
303 auto ph = REVPHHandles->Get(handle);
304 if (ph == nullptr) {
305 *status = HAL_HANDLE_ERROR;
306 return 0;
307 }
308
309 if (channel < 0 || channel > 1) {
310 *status = PARAMETER_OUT_OF_RANGE;
311 hal::SetLastErrorIndexOutOfRange(status, "Invalid REV Analog Index", 0, 2,
312 channel);
313 return 0;
314 }
315
316 PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
317
318 if (*status != 0) {
319 return 0;
320 }
321
322 if (channel == 1) {
323 return PH_status0_analog_0_decode(status0.analog_0);
324 }
325 return PH_status0_analog_1_decode(status0.analog_1);
326}
327
328int32_t HAL_GetREVPHSolenoids(HAL_REVPHHandle handle, int32_t* status) {
329 auto ph = REVPHHandles->Get(handle);
330 if (ph == nullptr) {
331 *status = HAL_HANDLE_ERROR;
332 return 0;
333 }
334
335 PH_status0_t status0 = HAL_REV_ReadPHStatus0(ph->hcan, status);
336
337 if (*status != 0) {
338 return 0;
339 }
340
341 uint32_t result = status0.channel_0;
342 result |= status0.channel_1 << 1;
343 result |= status0.channel_2 << 2;
344 result |= status0.channel_3 << 3;
345 result |= status0.channel_4 << 4;
346 result |= status0.channel_5 << 5;
347 result |= status0.channel_6 << 6;
348 result |= status0.channel_7 << 7;
349 result |= status0.channel_8 << 8;
350 result |= status0.channel_9 << 9;
351 result |= status0.channel_10 << 10;
352 result |= status0.channel_11 << 11;
353 result |= status0.channel_12 << 12;
354 result |= status0.channel_13 << 13;
355 result |= status0.channel_14 << 14;
356 result |= status0.channel_15 << 15;
357
358 return result;
359}
360
361void HAL_SetREVPHSolenoids(HAL_REVPHHandle handle, int32_t mask, int32_t values,
362 int32_t* status) {
363 auto ph = REVPHHandles->Get(handle);
364 if (ph == nullptr) {
365 *status = HAL_HANDLE_ERROR;
366 return;
367 }
368
369 std::scoped_lock lock{ph->solenoidLock};
370 for (int solenoid = 0; solenoid < kNumREVPHChannels; solenoid++) {
371 if (mask & (1 << solenoid)) {
372 // The mask bit for the solenoid is set, so we update the solenoid state
373 REV_SolenoidState desiredSolenoidState =
374 values & (1 << solenoid) ? kSolenoidEnabled : kSolenoidDisabled;
375 HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), solenoid,
376 desiredSolenoidState);
377 }
378 }
379 HAL_REV_SendSolenoidsState(ph.get(), status);
380}
381
382void HAL_FireREVPHOneShot(HAL_REVPHHandle handle, int32_t index, int32_t durMs,
383 int32_t* status) {
384 auto ph = REVPHHandles->Get(handle);
385 if (ph == nullptr) {
386 *status = HAL_HANDLE_ERROR;
387 return;
388 }
389
390 if (index >= kNumREVPHChannels || index < 0) {
391 *status = PARAMETER_OUT_OF_RANGE;
392 hal::SetLastError(
393 status,
394 fmt::format("Only [0-15] are valid index values. Requested {}", index));
395 return;
396 }
397
398 if (!HAL_REV_CheckPHPulseTime(durMs)) {
399 *status = PARAMETER_OUT_OF_RANGE;
400 hal::SetLastError(
401 status,
402 fmt::format("Time not within expected range [0-65534]. Requested {}",
403 durMs));
404 return;
405 }
406
407 {
408 std::scoped_lock lock{ph->solenoidLock};
409 HAL_REV_UpdateDesiredPHSolenoidState(ph.get(), index,
410 kSolenoidControlledViaPulse);
411 HAL_REV_SendSolenoidsState(ph.get(), status);
412 }
413
414 if (*status != 0) {
415 return;
416 }
417
418 PH_pulse_once_t pulse = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
419 pulse.pulse_length_ms = durMs;
420
421 // Specify which solenoid should be pulsed
422 // The protocol supports specifying any number of solenoids to be pulsed at
423 // the same time, should that functionality be exposed to users in the future.
424 switch (index) {
425 case 0:
426 pulse.channel_0 = true;
427 break;
428 case 1:
429 pulse.channel_1 = true;
430 break;
431 case 2:
432 pulse.channel_2 = true;
433 break;
434 case 3:
435 pulse.channel_3 = true;
436 break;
437 case 4:
438 pulse.channel_4 = true;
439 break;
440 case 5:
441 pulse.channel_5 = true;
442 break;
443 case 6:
444 pulse.channel_6 = true;
445 break;
446 case 7:
447 pulse.channel_7 = true;
448 break;
449 case 8:
450 pulse.channel_8 = true;
451 break;
452 case 9:
453 pulse.channel_9 = true;
454 break;
455 case 10:
456 pulse.channel_10 = true;
457 break;
458 case 11:
459 pulse.channel_11 = true;
460 break;
461 case 12:
462 pulse.channel_12 = true;
463 break;
464 case 13:
465 pulse.channel_13 = true;
466 break;
467 case 14:
468 pulse.channel_14 = true;
469 break;
470 case 15:
471 pulse.channel_15 = true;
472 break;
473 }
474
475 // Send pulse command
476 uint8_t packedData[PH_PULSE_ONCE_LENGTH] = {0};
477 PH_pulse_once_pack(packedData, &pulse, PH_PULSE_ONCE_LENGTH);
478 HAL_WriteCANPacket(ph->hcan, packedData, PH_PULSE_ONCE_LENGTH,
479 PH_PULSE_ONCE_FRAME_API, status);
480}