blob: 3a392f189e6b816d49069b020e461ac03bf7722f [file] [log] [blame]
Brian Silverman1a675112016-02-20 20:42:49 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016. 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
Brian Silverman26e4e522015-12-17 01:56:40 -05008#include "HAL/HAL.hpp"
9
10#include "HAL/Port.h"
11#include "HAL/Errors.hpp"
12#include "ctre/ctre.h"
13#include "visa/visa.h"
14#include "ChipObject.h"
15#include "FRC_NetworkCommunication/FRCComm.h"
16#include "FRC_NetworkCommunication/UsageReporting.h"
17#include "FRC_NetworkCommunication/LoadOut.h"
18#include "FRC_NetworkCommunication/CANSessionMux.h"
19#include <cstdlib>
20#include <fstream>
21#include <iostream>
Brian Silverman1a675112016-02-20 20:42:49 -050022#include <mutex>
Brian Silverman26e4e522015-12-17 01:56:40 -050023#include <unistd.h>
24#include <sys/prctl.h>
25#include <signal.h> // linux for kill
26const uint32_t solenoid_kNumDO7_0Elements = 8;
27const uint32_t dio_kNumSystems = tDIO::kNumSystems;
28const uint32_t interrupt_kNumSystems = tInterrupt::kNumSystems;
29const uint32_t kSystemClockTicksPerMicrosecond = 40;
30
31static tGlobal *global = nullptr;
32static tSysWatchdog *watchdog = nullptr;
33
Brian Silverman1a675112016-02-20 20:42:49 -050034static priority_mutex timeMutex;
35static priority_mutex msgMutex;
36static uint32_t timeEpoch = 0;
37static uint32_t prevFPGATime = 0;
38static void* rolloverNotifier = nullptr;
39
40extern "C" {
41
Brian Silverman26e4e522015-12-17 01:56:40 -050042void* getPort(uint8_t pin)
43{
44 Port* port = new Port();
45 port->pin = pin;
46 port->module = 1;
47 return port;
48}
49
50/**
51 * @deprecated Uses module numbers
52 */
53void* getPortWithModule(uint8_t module, uint8_t pin)
54{
55 Port* port = new Port();
56 port->pin = pin;
57 port->module = module;
58 return port;
59}
60
61void freePort(void* port_pointer)
62{
63 Port* port = (Port*) port_pointer;
64 delete port;
65}
66
67const char* getHALErrorMessage(int32_t code)
68{
69 switch(code) {
70 case 0:
71 return "";
72 case CTR_RxTimeout:
73 return CTR_RxTimeout_MESSAGE;
74 case CTR_TxTimeout:
75 return CTR_TxTimeout_MESSAGE;
76 case CTR_InvalidParamValue:
77 return CTR_InvalidParamValue_MESSAGE;
78 case CTR_UnexpectedArbId:
79 return CTR_UnexpectedArbId_MESSAGE;
80 case CTR_TxFailed:
81 return CTR_TxFailed_MESSAGE;
82 case CTR_SigNotUpdated:
83 return CTR_SigNotUpdated_MESSAGE;
84 case NiFpga_Status_FifoTimeout:
85 return NiFpga_Status_FifoTimeout_MESSAGE;
86 case NiFpga_Status_TransferAborted:
87 return NiFpga_Status_TransferAborted_MESSAGE;
88 case NiFpga_Status_MemoryFull:
89 return NiFpga_Status_MemoryFull_MESSAGE;
90 case NiFpga_Status_SoftwareFault:
91 return NiFpga_Status_SoftwareFault_MESSAGE;
92 case NiFpga_Status_InvalidParameter:
93 return NiFpga_Status_InvalidParameter_MESSAGE;
94 case NiFpga_Status_ResourceNotFound:
95 return NiFpga_Status_ResourceNotFound_MESSAGE;
96 case NiFpga_Status_ResourceNotInitialized:
97 return NiFpga_Status_ResourceNotInitialized_MESSAGE;
98 case NiFpga_Status_HardwareFault:
99 return NiFpga_Status_HardwareFault_MESSAGE;
100 case NiFpga_Status_IrqTimeout:
101 return NiFpga_Status_IrqTimeout_MESSAGE;
102 case SAMPLE_RATE_TOO_HIGH:
103 return SAMPLE_RATE_TOO_HIGH_MESSAGE;
104 case VOLTAGE_OUT_OF_RANGE:
105 return VOLTAGE_OUT_OF_RANGE_MESSAGE;
106 case LOOP_TIMING_ERROR:
107 return LOOP_TIMING_ERROR_MESSAGE;
108 case SPI_WRITE_NO_MOSI:
109 return SPI_WRITE_NO_MOSI_MESSAGE;
110 case SPI_READ_NO_MISO:
111 return SPI_READ_NO_MISO_MESSAGE;
112 case SPI_READ_NO_DATA:
113 return SPI_READ_NO_DATA_MESSAGE;
114 case INCOMPATIBLE_STATE:
115 return INCOMPATIBLE_STATE_MESSAGE;
116 case NO_AVAILABLE_RESOURCES:
117 return NO_AVAILABLE_RESOURCES_MESSAGE;
Brian Silverman1a675112016-02-20 20:42:49 -0500118 case RESOURCE_IS_ALLOCATED:
119 return RESOURCE_IS_ALLOCATED_MESSAGE;
Brian Silverman26e4e522015-12-17 01:56:40 -0500120 case NULL_PARAMETER:
121 return NULL_PARAMETER_MESSAGE;
122 case ANALOG_TRIGGER_LIMIT_ORDER_ERROR:
123 return ANALOG_TRIGGER_LIMIT_ORDER_ERROR_MESSAGE;
124 case ANALOG_TRIGGER_PULSE_OUTPUT_ERROR:
125 return ANALOG_TRIGGER_PULSE_OUTPUT_ERROR_MESSAGE;
126 case PARAMETER_OUT_OF_RANGE:
127 return PARAMETER_OUT_OF_RANGE_MESSAGE;
128 case ERR_CANSessionMux_InvalidBuffer:
129 return ERR_CANSessionMux_InvalidBuffer_MESSAGE;
130 case ERR_CANSessionMux_MessageNotFound:
131 return ERR_CANSessionMux_MessageNotFound_MESSAGE;
132 case WARN_CANSessionMux_NoToken:
133 return WARN_CANSessionMux_NoToken_MESSAGE;
134 case ERR_CANSessionMux_NotAllowed:
135 return ERR_CANSessionMux_NotAllowed_MESSAGE;
136 case ERR_CANSessionMux_NotInitialized:
137 return ERR_CANSessionMux_NotInitialized_MESSAGE;
138 case VI_ERROR_SYSTEM_ERROR:
139 return VI_ERROR_SYSTEM_ERROR_MESSAGE;
140 case VI_ERROR_INV_OBJECT:
141 return VI_ERROR_INV_OBJECT_MESSAGE;
142 case VI_ERROR_RSRC_LOCKED:
143 return VI_ERROR_RSRC_LOCKED_MESSAGE;
144 case VI_ERROR_RSRC_NFOUND:
145 return VI_ERROR_RSRC_NFOUND_MESSAGE;
146 case VI_ERROR_INV_RSRC_NAME:
147 return VI_ERROR_INV_RSRC_NAME_MESSAGE;
148 case VI_ERROR_QUEUE_OVERFLOW:
149 return VI_ERROR_QUEUE_OVERFLOW_MESSAGE;
150 case VI_ERROR_IO:
151 return VI_ERROR_IO_MESSAGE;
152 case VI_ERROR_ASRL_PARITY:
153 return VI_ERROR_ASRL_PARITY_MESSAGE;
154 case VI_ERROR_ASRL_FRAMING:
155 return VI_ERROR_ASRL_FRAMING_MESSAGE;
156 case VI_ERROR_ASRL_OVERRUN:
157 return VI_ERROR_ASRL_OVERRUN_MESSAGE;
158 case VI_ERROR_RSRC_BUSY:
159 return VI_ERROR_RSRC_BUSY_MESSAGE;
160 case VI_ERROR_INV_PARAMETER:
161 return VI_ERROR_INV_PARAMETER_MESSAGE;
162 default:
163 return "Unknown error status";
164 }
165}
166
167/**
168 * Return the FPGA Version number.
169 * For now, expect this to be competition year.
170 * @return FPGA Version number.
171 */
172uint16_t getFPGAVersion(int32_t *status)
173{
174 if (!global) {
175 *status = NiFpga_Status_ResourceNotInitialized;
176 return 0;
177 }
178 return global->readVersion(status);
179}
180
181/**
182 * Return the FPGA Revision number.
183 * The format of the revision is 3 numbers.
184 * The 12 most significant bits are the Major Revision.
185 * the next 8 bits are the Minor Revision.
186 * The 12 least significant bits are the Build Number.
187 * @return FPGA Revision number.
188 */
189uint32_t getFPGARevision(int32_t *status)
190{
191 if (!global) {
192 *status = NiFpga_Status_ResourceNotInitialized;
193 return 0;
194 }
195 return global->readRevision(status);
196}
197
198/**
199 * Read the microsecond-resolution timer on the FPGA.
200 *
201 * @return The current time in microseconds according to the FPGA (since FPGA reset).
202 */
Brian Silverman1a675112016-02-20 20:42:49 -0500203uint64_t getFPGATime(int32_t *status)
Brian Silverman26e4e522015-12-17 01:56:40 -0500204{
205 if (!global) {
206 *status = NiFpga_Status_ResourceNotInitialized;
207 return 0;
208 }
Brian Silverman1a675112016-02-20 20:42:49 -0500209 std::lock_guard<priority_mutex> lock(timeMutex);
210 uint32_t fpgaTime = global->readLocalTime(status);
211 if (*status != 0) return 0;
212 // check for rollover
213 if (fpgaTime < prevFPGATime) ++timeEpoch;
214 prevFPGATime = fpgaTime;
215 return (((uint64_t)timeEpoch) << 32) | ((uint64_t)fpgaTime);
Brian Silverman26e4e522015-12-17 01:56:40 -0500216}
217
218/**
219 * Get the state of the "USER" button on the RoboRIO
220 * @return true if the button is currently pressed down
221 */
222bool getFPGAButton(int32_t *status)
223{
224 if (!global) {
225 *status = NiFpga_Status_ResourceNotInitialized;
226 return false;
227 }
228 return global->readUserButton(status);
229}
230
231int HALSetErrorData(const char *errors, int errorsLength, int wait_ms)
232{
233 return setErrorData(errors, errorsLength, wait_ms);
234}
235
Brian Silverman1a675112016-02-20 20:42:49 -0500236int HALSendError(int isError, int32_t errorCode, int isLVCode,
237 const char *details, const char *location, const char *callStack,
238 int printMsg)
239{
240 // Avoid flooding console by keeping track of previous 5 error
241 // messages and only printing again if they're longer than 1 second old.
242 static constexpr int KEEP_MSGS = 5;
243 std::lock_guard<priority_mutex> lock(msgMutex);
244 static std::string prev_msg[KEEP_MSGS];
245 static uint64_t prev_msg_time[KEEP_MSGS] = { 0, 0, 0 };
246
247 int32_t status = 0;
248 uint64_t curTime = getFPGATime(&status);
249 int i;
250 for (i=0; i<KEEP_MSGS; ++i) {
251 if (prev_msg[i] == details) break;
252 }
253 int retval = 0;
254 if (i == KEEP_MSGS || (curTime - prev_msg_time[i]) >= 1000000) {
255 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode, details, location, callStack);
256 if (printMsg) {
257 if (location && location[0] != '\0') {
258 fprintf(stderr, "%s at %s: ",
259 isError ? "Error" : "Warning",
260 location);
261 }
262 fprintf(stderr, "%s\n", details);
263 if (callStack && callStack[0] != '\0') {
264 fprintf(stderr, "%s\n", callStack);
265 }
266 }
267 if (i == KEEP_MSGS) {
268 // replace the oldest one
269 i = 0;
270 uint64_t first = prev_msg_time[0];
271 for (int j=1; j<KEEP_MSGS; ++j) {
272 if (prev_msg_time[j] < first) {
273 first = prev_msg_time[j];
274 i = j;
275 }
276 }
277 prev_msg[i] = details;
278 }
279 prev_msg_time[i] = curTime;
280 }
281 return retval;
282}
283
Brian Silverman26e4e522015-12-17 01:56:40 -0500284
285bool HALGetSystemActive(int32_t *status)
286{
287 if (!watchdog) {
288 *status = NiFpga_Status_ResourceNotInitialized;
289 return false;
290 }
291 return watchdog->readStatus_SystemActive(status);
292}
293
294bool HALGetBrownedOut(int32_t *status)
295{
296 if (!watchdog) {
297 *status = NiFpga_Status_ResourceNotInitialized;
298 return false;
299 }
300 return !(watchdog->readStatus_PowerAlive(status));
301}
302
303static void HALCleanupAtExit() {
304 global = nullptr;
305 watchdog = nullptr;
306}
307
Brian Silverman1a675112016-02-20 20:42:49 -0500308static void timerRollover(uint64_t currentTime, void*) {
309 // reschedule timer for next rollover
310 int32_t status = 0;
311 updateNotifierAlarm(rolloverNotifier, currentTime + 0x80000000ULL, &status);
312}
313
Brian Silverman26e4e522015-12-17 01:56:40 -0500314/**
315 * Call this to start up HAL. This is required for robot programs.
316 */
317int HALInitialize(int mode)
318{
319 setlinebuf(stdin);
320 setlinebuf(stdout);
321
322 prctl(PR_SET_PDEATHSIG, SIGTERM);
323
324 FRC_NetworkCommunication_Reserve(nullptr);
325 // image 4; Fixes errors caused by multiple processes. Talk to NI about this
326 nFPGA::nRoboRIO_FPGANamespace::g_currentTargetClass =
327 nLoadOut::kTargetClass_RoboRIO;
328
329 int32_t status = 0;
330 global = tGlobal::create(&status);
331 watchdog = tSysWatchdog::create(&status);
332
333 std::atexit(HALCleanupAtExit);
334
Brian Silverman1a675112016-02-20 20:42:49 -0500335 if (!rolloverNotifier)
336 rolloverNotifier = initializeNotifier(timerRollover, nullptr, &status);
337 if (status == 0) {
338 uint64_t curTime = getFPGATime(&status);
339 if (status == 0)
340 updateNotifierAlarm(rolloverNotifier, curTime + 0x80000000ULL, &status);
341 }
342
Brian Silverman26e4e522015-12-17 01:56:40 -0500343 // Kill any previous robot programs
344 std::fstream fs;
345 // By making this both in/out, it won't give us an error if it doesnt exist
346 fs.open("/var/lock/frc.pid", std::fstream::in | std::fstream::out);
347 if (fs.bad())
348 return 0;
349
350 pid_t pid = 0;
351 if (!fs.eof() && !fs.fail())
352 {
353 fs >> pid;
354 //see if the pid is around, but we don't want to mess with init id=1, or ourselves
355 if (pid >= 2 && kill(pid, 0) == 0 && pid != getpid())
356 {
357 std::cout << "Killing previously running FRC program..."
358 << std::endl;
359 kill(pid, SIGTERM); // try to kill it
360 delayMillis(100);
361 if (kill(pid, 0) == 0)
362 {
363 // still not successfull
364 if (mode == 0)
365 {
366 std::cout << "FRC pid " << pid
367 << " did not die within 110ms. Aborting"
368 << std::endl;
369 return 0; // just fail
370 }
371 else if (mode == 1) // kill -9 it
372 kill(pid, SIGKILL);
373 else
374 {
375 std::cout << "WARNING: FRC pid " << pid
376 << " did not die within 110ms." << std::endl;
377 }
378 }
379
380 }
381 }
382 fs.close();
383 // we will re-open it write only to truncate the file
384 fs.open("/var/lock/frc.pid", std::fstream::out | std::fstream::trunc);
385 fs.seekp(0);
386 pid = getpid();
387 fs << pid << std::endl;
388 fs.close();
Brian Silverman1a675112016-02-20 20:42:49 -0500389
Brian Silverman26e4e522015-12-17 01:56:40 -0500390 return 1;
391}
392
393uint32_t HALReport(uint8_t resource, uint8_t instanceNumber, uint8_t context,
394 const char *feature)
395{
396 if(feature == NULL)
397 {
398 feature = "";
399 }
400
401 return FRC_NetworkCommunication_nUsageReporting_report(resource, instanceNumber, context, feature);
402}
403
404// TODO: HACKS
405void NumericArrayResize()
406{
407}
408void RTSetCleanupProc()
409{
410}
411void EDVR_CreateReference()
412{
413}
414void Occur()
415{
416}
417
418void imaqGetErrorText()
419{
420}
421void imaqGetLastError()
422{
423}
424void niTimestamp64()
425{
426}
Brian Silverman1a675112016-02-20 20:42:49 -0500427
428} // extern "C"