blob: 708ca1b929e6851f7cbaceeeb4bfde065c312550 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-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/HAL.h"
9
10#include <signal.h> // linux for kill
11#include <sys/prctl.h>
12#include <unistd.h>
13
14#include <atomic>
15#include <cstdlib>
16#include <fstream>
17#include <iostream>
18#include <mutex>
19#include <thread>
20
21#include "FRC_NetworkCommunication/CANSessionMux.h"
22#include "FRC_NetworkCommunication/FRCComm.h"
23#include "FRC_NetworkCommunication/LoadOut.h"
24#include "HAL/ChipObject.h"
25#include "HAL/DriverStation.h"
26#include "HAL/Errors.h"
27#include "HAL/Notifier.h"
28#include "HAL/cpp/priority_mutex.h"
29#include "HAL/handles/HandlesInternal.h"
30#include "ctre/ctre.h"
31#include "visa/visa.h"
32
33using namespace hal;
34
35static std::unique_ptr<tGlobal> global;
36static std::unique_ptr<tSysWatchdog> watchdog;
37
38static priority_mutex timeMutex;
39static uint32_t timeEpoch = 0;
40static uint32_t prevFPGATime = 0;
41static HAL_NotifierHandle rolloverNotifier = 0;
42
43using namespace hal;
44
45extern "C" {
46
47HAL_PortHandle HAL_GetPort(int32_t channel) {
48 // Dont allow a number that wouldn't fit in a uint8_t
49 if (channel < 0 || channel >= 255) return HAL_kInvalidHandle;
50 return createPortHandle(channel, 1);
51}
52
53/**
54 * @deprecated Uses module numbers
55 */
56HAL_PortHandle HAL_GetPortWithModule(int32_t module, int32_t channel) {
57 // Dont allow a number that wouldn't fit in a uint8_t
58 if (channel < 0 || channel >= 255) return HAL_kInvalidHandle;
59 if (module < 0 || module >= 255) return HAL_kInvalidHandle;
60 return createPortHandle(channel, module);
61}
62
63const char* HAL_GetErrorMessage(int32_t code) {
64 switch (code) {
65 case 0:
66 return "";
67 case CTR_RxTimeout:
68 return CTR_RxTimeout_MESSAGE;
69 case CTR_TxTimeout:
70 return CTR_TxTimeout_MESSAGE;
71 case CTR_InvalidParamValue:
72 return CTR_InvalidParamValue_MESSAGE;
73 case CTR_UnexpectedArbId:
74 return CTR_UnexpectedArbId_MESSAGE;
75 case CTR_TxFailed:
76 return CTR_TxFailed_MESSAGE;
77 case CTR_SigNotUpdated:
78 return CTR_SigNotUpdated_MESSAGE;
79 case NiFpga_Status_FifoTimeout:
80 return NiFpga_Status_FifoTimeout_MESSAGE;
81 case NiFpga_Status_TransferAborted:
82 return NiFpga_Status_TransferAborted_MESSAGE;
83 case NiFpga_Status_MemoryFull:
84 return NiFpga_Status_MemoryFull_MESSAGE;
85 case NiFpga_Status_SoftwareFault:
86 return NiFpga_Status_SoftwareFault_MESSAGE;
87 case NiFpga_Status_InvalidParameter:
88 return NiFpga_Status_InvalidParameter_MESSAGE;
89 case NiFpga_Status_ResourceNotFound:
90 return NiFpga_Status_ResourceNotFound_MESSAGE;
91 case NiFpga_Status_ResourceNotInitialized:
92 return NiFpga_Status_ResourceNotInitialized_MESSAGE;
93 case NiFpga_Status_HardwareFault:
94 return NiFpga_Status_HardwareFault_MESSAGE;
95 case NiFpga_Status_IrqTimeout:
96 return NiFpga_Status_IrqTimeout_MESSAGE;
97 case SAMPLE_RATE_TOO_HIGH:
98 return SAMPLE_RATE_TOO_HIGH_MESSAGE;
99 case VOLTAGE_OUT_OF_RANGE:
100 return VOLTAGE_OUT_OF_RANGE_MESSAGE;
101 case LOOP_TIMING_ERROR:
102 return LOOP_TIMING_ERROR_MESSAGE;
103 case SPI_WRITE_NO_MOSI:
104 return SPI_WRITE_NO_MOSI_MESSAGE;
105 case SPI_READ_NO_MISO:
106 return SPI_READ_NO_MISO_MESSAGE;
107 case SPI_READ_NO_DATA:
108 return SPI_READ_NO_DATA_MESSAGE;
109 case INCOMPATIBLE_STATE:
110 return INCOMPATIBLE_STATE_MESSAGE;
111 case NO_AVAILABLE_RESOURCES:
112 return NO_AVAILABLE_RESOURCES_MESSAGE;
113 case RESOURCE_IS_ALLOCATED:
114 return RESOURCE_IS_ALLOCATED_MESSAGE;
115 case RESOURCE_OUT_OF_RANGE:
116 return RESOURCE_OUT_OF_RANGE_MESSAGE;
117 case HAL_INVALID_ACCUMULATOR_CHANNEL:
118 return HAL_INVALID_ACCUMULATOR_CHANNEL_MESSAGE;
119 case HAL_HANDLE_ERROR:
120 return HAL_HANDLE_ERROR_MESSAGE;
121 case NULL_PARAMETER:
122 return NULL_PARAMETER_MESSAGE;
123 case ANALOG_TRIGGER_LIMIT_ORDER_ERROR:
124 return ANALOG_TRIGGER_LIMIT_ORDER_ERROR_MESSAGE;
125 case ANALOG_TRIGGER_PULSE_OUTPUT_ERROR:
126 return ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE;
127 case PARAMETER_OUT_OF_RANGE:
128 return PARAMETER_OUT_OF_RANGE_MESSAGE;
129 case HAL_COUNTER_NOT_SUPPORTED:
130 return HAL_COUNTER_NOT_SUPPORTED_MESSAGE;
131 case ERR_CANSessionMux_InvalidBuffer:
132 return ERR_CANSessionMux_InvalidBuffer_MESSAGE;
133 case ERR_CANSessionMux_MessageNotFound:
134 return ERR_CANSessionMux_MessageNotFound_MESSAGE;
135 case WARN_CANSessionMux_NoToken:
136 return WARN_CANSessionMux_NoToken_MESSAGE;
137 case ERR_CANSessionMux_NotAllowed:
138 return ERR_CANSessionMux_NotAllowed_MESSAGE;
139 case ERR_CANSessionMux_NotInitialized:
140 return ERR_CANSessionMux_NotInitialized_MESSAGE;
141 case VI_ERROR_SYSTEM_ERROR:
142 return VI_ERROR_SYSTEM_ERROR_MESSAGE;
143 case VI_ERROR_INV_OBJECT:
144 return VI_ERROR_INV_OBJECT_MESSAGE;
145 case VI_ERROR_RSRC_LOCKED:
146 return VI_ERROR_RSRC_LOCKED_MESSAGE;
147 case VI_ERROR_RSRC_NFOUND:
148 return VI_ERROR_RSRC_NFOUND_MESSAGE;
149 case VI_ERROR_INV_RSRC_NAME:
150 return VI_ERROR_INV_RSRC_NAME_MESSAGE;
151 case VI_ERROR_QUEUE_OVERFLOW:
152 return VI_ERROR_QUEUE_OVERFLOW_MESSAGE;
153 case VI_ERROR_IO:
154 return VI_ERROR_IO_MESSAGE;
155 case VI_ERROR_ASRL_PARITY:
156 return VI_ERROR_ASRL_PARITY_MESSAGE;
157 case VI_ERROR_ASRL_FRAMING:
158 return VI_ERROR_ASRL_FRAMING_MESSAGE;
159 case VI_ERROR_ASRL_OVERRUN:
160 return VI_ERROR_ASRL_OVERRUN_MESSAGE;
161 case VI_ERROR_RSRC_BUSY:
162 return VI_ERROR_RSRC_BUSY_MESSAGE;
163 case VI_ERROR_INV_PARAMETER:
164 return VI_ERROR_INV_PARAMETER_MESSAGE;
165 case HAL_PWM_SCALE_ERROR:
166 return HAL_PWM_SCALE_ERROR_MESSAGE;
167 case HAL_SERIAL_PORT_NOT_FOUND:
168 return HAL_SERIAL_PORT_NOT_FOUND_MESSAGE;
169 case HAL_THREAD_PRIORITY_ERROR:
170 return HAL_THREAD_PRIORITY_ERROR_MESSAGE;
171 case HAL_THREAD_PRIORITY_RANGE_ERROR:
172 return HAL_THREAD_PRIORITY_RANGE_ERROR_MESSAGE;
173 case HAL_SERIAL_PORT_OPEN_ERROR:
174 return HAL_SERIAL_PORT_OPEN_ERROR_MESSAGE;
175 case HAL_SERIAL_PORT_ERROR:
176 return HAL_SERIAL_PORT_ERROR_MESSAGE;
177 default:
178 return "Unknown error status";
179 }
180}
181
182/**
183 * Returns the runtime type of this HAL
184 */
185HAL_RuntimeType HAL_GetRuntimeType() { return HAL_Athena; }
186
187/**
188 * Return the FPGA Version number.
189 * For now, expect this to be competition year.
190 * @return FPGA Version number.
191 */
192int32_t HAL_GetFPGAVersion(int32_t* status) {
193 if (!global) {
194 *status = NiFpga_Status_ResourceNotInitialized;
195 return 0;
196 }
197 return global->readVersion(status);
198}
199
200/**
201 * Return the FPGA Revision number.
202 * The format of the revision is 3 numbers.
203 * The 12 most significant bits are the Major Revision.
204 * the next 8 bits are the Minor Revision.
205 * The 12 least significant bits are the Build Number.
206 * @return FPGA Revision number.
207 */
208int64_t HAL_GetFPGARevision(int32_t* status) {
209 if (!global) {
210 *status = NiFpga_Status_ResourceNotInitialized;
211 return 0;
212 }
213 return global->readRevision(status);
214}
215
216/**
217 * Read the microsecond-resolution timer on the FPGA.
218 *
219 * @return The current time in microseconds according to the FPGA (since FPGA
220 * reset).
221 */
222uint64_t HAL_GetFPGATime(int32_t* status) {
223 if (!global) {
224 *status = NiFpga_Status_ResourceNotInitialized;
225 return 0;
226 }
227 std::lock_guard<priority_mutex> lock(timeMutex);
228 uint32_t fpgaTime = global->readLocalTime(status);
229 if (*status != 0) return 0;
230 // check for rollover
231 if (fpgaTime < prevFPGATime) ++timeEpoch;
232 prevFPGATime = fpgaTime;
233 return static_cast<uint64_t>(timeEpoch) << 32 |
234 static_cast<uint64_t>(fpgaTime);
235}
236
237/**
238 * Get the state of the "USER" button on the roboRIO
239 * @return true if the button is currently pressed down
240 */
241HAL_Bool HAL_GetFPGAButton(int32_t* status) {
242 if (!global) {
243 *status = NiFpga_Status_ResourceNotInitialized;
244 return false;
245 }
246 return global->readUserButton(status);
247}
248
249HAL_Bool HAL_GetSystemActive(int32_t* status) {
250 if (!watchdog) {
251 *status = NiFpga_Status_ResourceNotInitialized;
252 return false;
253 }
254 return watchdog->readStatus_SystemActive(status);
255}
256
257HAL_Bool HAL_GetBrownedOut(int32_t* status) {
258 if (!watchdog) {
259 *status = NiFpga_Status_ResourceNotInitialized;
260 return false;
261 }
262 return !(watchdog->readStatus_PowerAlive(status));
263}
264
265static void timerRollover(uint64_t currentTime, HAL_NotifierHandle handle) {
266 // reschedule timer for next rollover
267 int32_t status = 0;
268 HAL_UpdateNotifierAlarm(handle, currentTime + 0x80000000ULL, &status);
269}
270
271void HAL_BaseInitialize(int32_t* status) {
272 static std::atomic_bool initialized{false};
273 static priority_mutex initializeMutex;
274 // Initial check, as if it's true initialization has finished
275 if (initialized) return;
276
277 std::lock_guard<priority_mutex> lock(initializeMutex);
278 // Second check in case another thread was waiting
279 if (initialized) return;
280 // image 4; Fixes errors caused by multiple processes. Talk to NI about this
281 nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
282 nLoadOut::kTargetClass_RoboRIO;
283
284 global.reset(tGlobal::create(status));
285 watchdog.reset(tSysWatchdog::create(status));
286 initialized = true;
287}
288
289/**
290 * Call this to start up HAL. This is required for robot programs.
291 */
292int32_t HAL_Initialize(int32_t mode) {
293 setlinebuf(stdin);
294 setlinebuf(stdout);
295
296 prctl(PR_SET_PDEATHSIG, SIGTERM);
297
298 FRC_NetworkCommunication_Reserve(nullptr);
299
300 std::atexit([]() {
301 // Unregister our new data condition variable.
302 setNewDataSem(nullptr);
303 });
304
305 int32_t status = 0;
306 HAL_BaseInitialize(&status);
307
308 if (!rolloverNotifier)
309 rolloverNotifier = HAL_InitializeNotifier(timerRollover, nullptr, &status);
310 if (status == 0) {
311 uint64_t curTime = HAL_GetFPGATime(&status);
312 if (status == 0)
313 HAL_UpdateNotifierAlarm(rolloverNotifier, curTime + 0x80000000ULL,
314 &status);
315 }
316
317 // Kill any previous robot programs
318 std::fstream fs;
319 // By making this both in/out, it won't give us an error if it doesnt exist
320 fs.open("/var/lock/frc.pid", std::fstream::in | std::fstream::out);
321 if (fs.bad()) return 0;
322
323 pid_t pid = 0;
324 if (!fs.eof() && !fs.fail()) {
325 fs >> pid;
326 // see if the pid is around, but we don't want to mess with init id=1, or
327 // ourselves
328 if (pid >= 2 && kill(pid, 0) == 0 && pid != getpid()) {
329 std::cout << "Killing previously running FRC program..." << std::endl;
330 kill(pid, SIGTERM); // try to kill it
331 std::this_thread::sleep_for(std::chrono::milliseconds(100));
332 if (kill(pid, 0) == 0) {
333 // still not successfull
334 if (mode == 0) {
335 std::cout << "FRC pid " << pid
336 << " did not die within 110ms. Aborting" << std::endl;
337 return 0; // just fail
338 } else if (mode == 1) { // kill -9 it
339 kill(pid, SIGKILL);
340 } else {
341 std::cout << "WARNING: FRC pid " << pid
342 << " did not die within 110ms." << std::endl;
343 }
344 }
345 }
346 }
347 fs.close();
348 // we will re-open it write only to truncate the file
349 fs.open("/var/lock/frc.pid", std::fstream::out | std::fstream::trunc);
350 fs.seekp(0);
351 pid = getpid();
352 fs << pid << std::endl;
353 fs.close();
354
355 HAL_InitializeDriverStation();
356
357 return 1;
358}
359
360int64_t HAL_Report(int32_t resource, int32_t instanceNumber, int32_t context,
361 const char* feature) {
362 if (feature == nullptr) {
363 feature = "";
364 }
365
366 return FRC_NetworkCommunication_nUsageReporting_report(
367 resource, instanceNumber, context, feature);
368}
369
370// TODO: HACKS
371// No need for header definitions, as we should not run from user code.
372void NumericArrayResize() {}
373void RTSetCleanupProc() {}
374void EDVR_CreateReference() {}
375void Occur() {}
376
377} // extern "C"