blob: 51fe327fa47e962016a671eafaae1a1869f9fe98 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2016-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -08003/* 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 <atomic>
9#include <chrono>
10#include <cstdlib>
11#include <cstring>
12#include <limits>
13
14#include <FRC_NetworkCommunication/FRCComm.h>
15#include <FRC_NetworkCommunication/NetCommRPCProxy_Occur.h>
16#include <wpi/SafeThread.h>
17#include <wpi/condition_variable.h>
18#include <wpi/mutex.h>
19#include <wpi/raw_ostream.h>
20
21#include "hal/DriverStation.h"
22
23static_assert(sizeof(int32_t) >= sizeof(int),
24 "FRC_NetworkComm status variable is larger than 32 bits");
25
26struct HAL_JoystickAxesInt {
27 int16_t count;
28 int16_t axes[HAL_kMaxJoystickAxes];
29};
30
31static constexpr int kJoystickPorts = 6;
32
33// Message and Data variables
34static wpi::mutex msgMutex;
35
36static int32_t HAL_GetJoystickAxesInternal(int32_t joystickNum,
37 HAL_JoystickAxes* axes) {
38 HAL_JoystickAxesInt axesInt;
39
40 int retVal = FRC_NetworkCommunication_getJoystickAxes(
41 joystickNum, reinterpret_cast<JoystickAxes_t*>(&axesInt),
42 HAL_kMaxJoystickAxes);
43
44 // copy integer values to double values
45 axes->count = axesInt.count;
46 // current scaling is -128 to 127, can easily be patched in the future by
47 // changing this function.
48 for (int32_t i = 0; i < axesInt.count; i++) {
49 int8_t value = axesInt.axes[i];
50 if (value < 0) {
51 axes->axes[i] = value / 128.0;
52 } else {
53 axes->axes[i] = value / 127.0;
54 }
55 }
56
57 return retVal;
58}
59
60static int32_t HAL_GetJoystickPOVsInternal(int32_t joystickNum,
61 HAL_JoystickPOVs* povs) {
62 return FRC_NetworkCommunication_getJoystickPOVs(
63 joystickNum, reinterpret_cast<JoystickPOV_t*>(povs),
64 HAL_kMaxJoystickPOVs);
65}
66
67static int32_t HAL_GetJoystickButtonsInternal(int32_t joystickNum,
68 HAL_JoystickButtons* buttons) {
69 return FRC_NetworkCommunication_getJoystickButtons(
70 joystickNum, &buttons->buttons, &buttons->count);
71}
72/**
73 * Retrieve the Joystick Descriptor for particular slot
74 * @param desc [out] descriptor (data transfer object) to fill in. desc is
75 * filled in regardless of success. In other words, if descriptor is not
76 * available, desc is filled in with default values matching the init-values in
77 * Java and C++ Driverstation for when caller requests a too-large joystick
78 * index.
79 *
80 * @return error code reported from Network Comm back-end. Zero is good,
81 * nonzero is bad.
82 */
83static int32_t HAL_GetJoystickDescriptorInternal(int32_t joystickNum,
84 HAL_JoystickDescriptor* desc) {
85 desc->isXbox = 0;
86 desc->type = (std::numeric_limits<uint8_t>::max)();
87 desc->name[0] = '\0';
88 desc->axisCount =
89 HAL_kMaxJoystickAxes; /* set to the desc->axisTypes's capacity */
90 desc->buttonCount = 0;
91 desc->povCount = 0;
92 int retval = FRC_NetworkCommunication_getJoystickDesc(
93 joystickNum, &desc->isXbox, &desc->type,
94 reinterpret_cast<char*>(&desc->name), &desc->axisCount,
95 reinterpret_cast<uint8_t*>(&desc->axisTypes), &desc->buttonCount,
96 &desc->povCount);
97 /* check the return, if there is an error and the RIOimage predates FRC2017,
98 * then axisCount needs to be cleared */
99 if (retval != 0) {
100 /* set count to zero so downstream code doesn't decode invalid axisTypes. */
101 desc->axisCount = 0;
102 }
103 return retval;
104}
105
106static int32_t HAL_GetControlWordInternal(HAL_ControlWord* controlWord) {
107 std::memset(controlWord, 0, sizeof(HAL_ControlWord));
108 return FRC_NetworkCommunication_getControlWord(
109 reinterpret_cast<ControlWord_t*>(controlWord));
110}
111
112static int32_t HAL_GetMatchInfoInternal(HAL_MatchInfo* info) {
113 MatchType_t matchType = MatchType_t::kMatchType_none;
114 int status = FRC_NetworkCommunication_getMatchInfo(
115 info->eventName, &matchType, &info->matchNumber, &info->replayNumber,
116 info->gameSpecificMessage, &info->gameSpecificMessageSize);
117
118 info->matchType = static_cast<HAL_MatchType>(matchType);
119
120 *(std::end(info->eventName) - 1) = '\0';
121
122 return status;
123}
124
125static wpi::mutex* newDSDataAvailableMutex;
126static wpi::condition_variable* newDSDataAvailableCond;
Austin Schuh1e69f942020-11-14 15:06:14 -0800127static int newDSDataAvailableCounter{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800128
129namespace hal {
130namespace init {
131void InitializeFRCDriverStation() {
132 static wpi::mutex newMutex;
133 newDSDataAvailableMutex = &newMutex;
134 static wpi::condition_variable newCond;
135 newDSDataAvailableCond = &newCond;
136}
137} // namespace init
138} // namespace hal
139
140extern "C" {
141
142int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
143 const char* details, const char* location,
144 const char* callStack, HAL_Bool printMsg) {
145 // Avoid flooding console by keeping track of previous 5 error
146 // messages and only printing again if they're longer than 1 second old.
147 static constexpr int KEEP_MSGS = 5;
148 std::scoped_lock lock(msgMutex);
149 static std::string prevMsg[KEEP_MSGS];
150 static std::chrono::time_point<std::chrono::steady_clock>
151 prevMsgTime[KEEP_MSGS];
152 static bool initialized = false;
153 if (!initialized) {
154 for (int i = 0; i < KEEP_MSGS; i++) {
155 prevMsgTime[i] =
156 std::chrono::steady_clock::now() - std::chrono::seconds(2);
157 }
158 initialized = true;
159 }
160
161 auto curTime = std::chrono::steady_clock::now();
162 int i;
163 for (i = 0; i < KEEP_MSGS; ++i) {
164 if (prevMsg[i] == details) break;
165 }
166 int retval = 0;
167 if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
168 wpi::StringRef detailsRef{details};
169 wpi::StringRef locationRef{location};
170 wpi::StringRef callStackRef{callStack};
171
172 // 1 tag, 4 timestamp, 2 seqnum
173 // 2 numOccur, 4 error code, 1 flags, 6 strlen
174 // 1 extra needed for padding on Netcomm end.
175 size_t baseLength = 21;
176
177 if (baseLength + detailsRef.size() + locationRef.size() +
178 callStackRef.size() <=
Austin Schuh1e69f942020-11-14 15:06:14 -0800179 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800180 // Pass through
181 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
182 details, location, callStack);
Austin Schuh1e69f942020-11-14 15:06:14 -0800183 } else if (baseLength + detailsRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800184 // Details too long, cut both location and stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800185 auto newLen = 65535 - baseLength;
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 std::string newDetails{details, newLen};
187 char empty = '\0';
188 retval = FRC_NetworkCommunication_sendError(
189 isError, errorCode, isLVCode, newDetails.c_str(), &empty, &empty);
Austin Schuh1e69f942020-11-14 15:06:14 -0800190 } else if (baseLength + detailsRef.size() + locationRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800191 // Location too long, cut stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800192 auto newLen = 65535 - baseLength - detailsRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800193 std::string newLocation{location, newLen};
194 char empty = '\0';
195 retval = FRC_NetworkCommunication_sendError(
196 isError, errorCode, isLVCode, details, newLocation.c_str(), &empty);
197 } else {
198 // Stack too long
Austin Schuh1e69f942020-11-14 15:06:14 -0800199 auto newLen = 65535 - baseLength - detailsRef.size() - locationRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800200 std::string newCallStack{callStack, newLen};
201 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
202 details, location,
203 newCallStack.c_str());
204 }
205 if (printMsg) {
206 if (location && location[0] != '\0') {
207 wpi::errs() << (isError ? "Error" : "Warning") << " at " << location
208 << ": ";
209 }
210 wpi::errs() << details << "\n";
211 if (callStack && callStack[0] != '\0') {
212 wpi::errs() << callStack << "\n";
213 }
214 }
215 if (i == KEEP_MSGS) {
216 // replace the oldest one
217 i = 0;
218 auto first = prevMsgTime[0];
219 for (int j = 1; j < KEEP_MSGS; ++j) {
220 if (prevMsgTime[j] < first) {
221 first = prevMsgTime[j];
222 i = j;
223 }
224 }
225 prevMsg[i] = details;
226 }
227 prevMsgTime[i] = curTime;
228 }
229 return retval;
230}
231
Austin Schuh1e69f942020-11-14 15:06:14 -0800232int32_t HAL_SendConsoleLine(const char* line) {
233 wpi::StringRef lineRef{line};
234 if (lineRef.size() <= 65535) {
235 // Send directly
236 return FRC_NetworkCommunication_sendConsoleLine(line);
237 } else {
238 // Need to truncate
239 std::string newLine{line, 65535};
240 return FRC_NetworkCommunication_sendConsoleLine(newLine.c_str());
241 }
242}
243
Brian Silverman8fce7482020-01-05 13:18:21 -0800244int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
245 return HAL_GetControlWordInternal(controlWord);
246}
247
248int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
249 return HAL_GetJoystickAxesInternal(joystickNum, axes);
250}
251
252int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
253 return HAL_GetJoystickPOVsInternal(joystickNum, povs);
254}
255
256int32_t HAL_GetJoystickButtons(int32_t joystickNum,
257 HAL_JoystickButtons* buttons) {
258 return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
259}
260
261int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
262 HAL_JoystickDescriptor* desc) {
263 return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
264}
265
266int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
267 return HAL_GetMatchInfoInternal(info);
268}
269
270HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
271 HAL_AllianceStationID allianceStation;
272 *status = FRC_NetworkCommunication_getAllianceStation(
273 reinterpret_cast<AllianceStationID_t*>(&allianceStation));
274 return allianceStation;
275}
276
277HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
278 HAL_JoystickDescriptor joystickDesc;
279 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
280 return 0;
281 } else {
282 return joystickDesc.isXbox;
283 }
284}
285
286int32_t HAL_GetJoystickType(int32_t joystickNum) {
287 HAL_JoystickDescriptor joystickDesc;
288 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
289 return -1;
290 } else {
291 return joystickDesc.type;
292 }
293}
294
295char* HAL_GetJoystickName(int32_t joystickNum) {
296 HAL_JoystickDescriptor joystickDesc;
297 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
298 char* name = static_cast<char*>(std::malloc(1));
299 name[0] = '\0';
300 return name;
301 } else {
302 size_t len = std::strlen(joystickDesc.name);
303 char* name = static_cast<char*>(std::malloc(len + 1));
304 std::strncpy(name, joystickDesc.name, len);
305 name[len] = '\0';
306 return name;
307 }
308}
309
310void HAL_FreeJoystickName(char* name) { std::free(name); }
311
312int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
313 HAL_JoystickDescriptor joystickDesc;
314 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
315 return -1;
316 } else {
317 return joystickDesc.axisTypes[axis];
318 }
319}
320
321int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
322 int32_t leftRumble, int32_t rightRumble) {
323 return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs,
324 leftRumble, rightRumble);
325}
326
327double HAL_GetMatchTime(int32_t* status) {
328 float matchTime;
329 *status = FRC_NetworkCommunication_getMatchTime(&matchTime);
330 return matchTime;
331}
332
333void HAL_ObserveUserProgramStarting(void) {
334 FRC_NetworkCommunication_observeUserProgramStarting();
335}
336
337void HAL_ObserveUserProgramDisabled(void) {
338 FRC_NetworkCommunication_observeUserProgramDisabled();
339}
340
341void HAL_ObserveUserProgramAutonomous(void) {
342 FRC_NetworkCommunication_observeUserProgramAutonomous();
343}
344
345void HAL_ObserveUserProgramTeleop(void) {
346 FRC_NetworkCommunication_observeUserProgramTeleop();
347}
348
349void HAL_ObserveUserProgramTest(void) {
350 FRC_NetworkCommunication_observeUserProgramTest();
351}
352
353static int& GetThreadLocalLastCount() {
354 // There is a rollover error condition here. At Packet# = n * (uintmax), this
355 // will return false when instead it should return true. However, this at a
356 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
357 // worth the cycles to check.
Austin Schuh1e69f942020-11-14 15:06:14 -0800358 thread_local int lastCount{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800359 return lastCount;
360}
361
Brian Silverman8fce7482020-01-05 13:18:21 -0800362HAL_Bool HAL_IsNewControlData(void) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800363 std::scoped_lock lock{*newDSDataAvailableMutex};
Brian Silverman8fce7482020-01-05 13:18:21 -0800364 int& lastCount = GetThreadLocalLastCount();
Austin Schuh1e69f942020-11-14 15:06:14 -0800365 int currentCount = newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800366 if (lastCount == currentCount) return false;
367 lastCount = currentCount;
368 return true;
369}
370
371/**
372 * Waits for the newest DS packet to arrive. Note that this is a blocking call.
373 */
374void HAL_WaitForDSData(void) { HAL_WaitForDSDataTimeout(0); }
375
376/**
377 * Waits for the newest DS packet to arrive. If timeout is <= 0, this will wait
378 * forever. Otherwise, it will wait until either a new packet, or the timeout
379 * time has passed. Returns true on new data, false on timeout.
380 */
381HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800382 std::unique_lock lock{*newDSDataAvailableMutex};
383 int& lastCount = GetThreadLocalLastCount();
384 int currentCount = newDSDataAvailableCounter;
385 if (lastCount != currentCount) {
386 lastCount = currentCount;
387 return true;
388 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800389 auto timeoutTime =
390 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
391
Austin Schuh1e69f942020-11-14 15:06:14 -0800392 while (newDSDataAvailableCounter == currentCount) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800393 if (timeout > 0) {
394 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
395 if (timedOut == std::cv_status::timeout) {
396 return false;
397 }
398 } else {
399 newDSDataAvailableCond->wait(lock);
400 }
401 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800402 lastCount = newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800403 return true;
404}
405
406// Constant number to be used for our occur handle
407constexpr int32_t refNumber = 42;
408
409static void newDataOccur(uint32_t refNum) {
410 // Since we could get other values, require our specific handle
411 // to signal our threads
412 if (refNum != refNumber) return;
Austin Schuh1e69f942020-11-14 15:06:14 -0800413 std::scoped_lock lock{*newDSDataAvailableMutex};
Brian Silverman8fce7482020-01-05 13:18:21 -0800414 // Notify all threads
Austin Schuh1e69f942020-11-14 15:06:14 -0800415 ++newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800416 newDSDataAvailableCond->notify_all();
417}
418
419/*
420 * Call this to initialize the driver station communication. This will properly
421 * handle multiple calls. However note that this CANNOT be called from a library
422 * that interfaces with LabVIEW.
423 */
424void HAL_InitializeDriverStation(void) {
425 static std::atomic_bool initialized{false};
426 static wpi::mutex initializeMutex;
427 // Initial check, as if it's true initialization has finished
428 if (initialized) return;
429
430 std::scoped_lock lock(initializeMutex);
431 // Second check in case another thread was waiting
432 if (initialized) return;
433
434 // Set up the occur function internally with NetComm
435 NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
436 // Set up our occur reference number
437 setNewDataOccurRef(refNumber);
438
439 initialized = true;
440}
441
442/*
443 * Releases the DS Mutex to allow proper shutdown of any threads that are
444 * waiting on it.
445 */
446void HAL_ReleaseDSMutex(void) { newDataOccur(refNumber); }
447
448} // extern "C"