blob: 0f4b69b46c83a10b856ed6f406310fedd8dba150 [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.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include <atomic>
6#include <chrono>
7#include <cstdlib>
8#include <cstring>
9#include <limits>
Austin Schuh812d0d12021-11-04 20:16:48 -070010#include <string>
11#include <string_view>
Brian Silverman8fce7482020-01-05 13:18:21 -080012
13#include <FRC_NetworkCommunication/FRCComm.h>
14#include <FRC_NetworkCommunication/NetCommRPCProxy_Occur.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080016#include <wpi/SafeThread.h>
17#include <wpi/condition_variable.h>
18#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080019
20#include "hal/DriverStation.h"
21
22static_assert(sizeof(int32_t) >= sizeof(int),
23 "FRC_NetworkComm status variable is larger than 32 bits");
24
25struct HAL_JoystickAxesInt {
26 int16_t count;
27 int16_t axes[HAL_kMaxJoystickAxes];
28};
29
30static constexpr int kJoystickPorts = 6;
31
32// Message and Data variables
33static wpi::mutex msgMutex;
34
35static int32_t HAL_GetJoystickAxesInternal(int32_t joystickNum,
36 HAL_JoystickAxes* axes) {
37 HAL_JoystickAxesInt axesInt;
38
39 int retVal = FRC_NetworkCommunication_getJoystickAxes(
40 joystickNum, reinterpret_cast<JoystickAxes_t*>(&axesInt),
41 HAL_kMaxJoystickAxes);
42
43 // copy integer values to double values
44 axes->count = axesInt.count;
45 // current scaling is -128 to 127, can easily be patched in the future by
46 // changing this function.
47 for (int32_t i = 0; i < axesInt.count; i++) {
48 int8_t value = axesInt.axes[i];
49 if (value < 0) {
50 axes->axes[i] = value / 128.0;
51 } else {
52 axes->axes[i] = value / 127.0;
53 }
54 }
55
56 return retVal;
57}
58
59static int32_t HAL_GetJoystickPOVsInternal(int32_t joystickNum,
60 HAL_JoystickPOVs* povs) {
61 return FRC_NetworkCommunication_getJoystickPOVs(
62 joystickNum, reinterpret_cast<JoystickPOV_t*>(povs),
63 HAL_kMaxJoystickPOVs);
64}
65
66static int32_t HAL_GetJoystickButtonsInternal(int32_t joystickNum,
67 HAL_JoystickButtons* buttons) {
68 return FRC_NetworkCommunication_getJoystickButtons(
69 joystickNum, &buttons->buttons, &buttons->count);
70}
71/**
Austin Schuh812d0d12021-11-04 20:16:48 -070072 * Retrieve the Joystick Descriptor for particular slot.
Brian Silverman8fce7482020-01-05 13:18:21 -080073 *
Austin Schuh812d0d12021-11-04 20:16:48 -070074 * @param[out] desc descriptor (data transfer object) to fill in. desc is filled
75 * in regardless of success. In other words, if descriptor is
76 * not available, desc is filled in with default values
77 * matching the init-values in Java and C++ Driverstation for
78 * when caller requests a too-large joystick index.
Brian Silverman8fce7482020-01-05 13:18:21 -080079 * @return error code reported from Network Comm back-end. Zero is good,
Austin Schuh812d0d12021-11-04 20:16:48 -070080 * nonzero is bad.
Brian Silverman8fce7482020-01-05 13:18:21 -080081 */
82static int32_t HAL_GetJoystickDescriptorInternal(int32_t joystickNum,
83 HAL_JoystickDescriptor* desc) {
84 desc->isXbox = 0;
85 desc->type = (std::numeric_limits<uint8_t>::max)();
86 desc->name[0] = '\0';
87 desc->axisCount =
88 HAL_kMaxJoystickAxes; /* set to the desc->axisTypes's capacity */
89 desc->buttonCount = 0;
90 desc->povCount = 0;
91 int retval = FRC_NetworkCommunication_getJoystickDesc(
92 joystickNum, &desc->isXbox, &desc->type,
93 reinterpret_cast<char*>(&desc->name), &desc->axisCount,
94 reinterpret_cast<uint8_t*>(&desc->axisTypes), &desc->buttonCount,
95 &desc->povCount);
96 /* check the return, if there is an error and the RIOimage predates FRC2017,
97 * then axisCount needs to be cleared */
98 if (retval != 0) {
99 /* set count to zero so downstream code doesn't decode invalid axisTypes. */
100 desc->axisCount = 0;
101 }
102 return retval;
103}
104
105static int32_t HAL_GetControlWordInternal(HAL_ControlWord* controlWord) {
106 std::memset(controlWord, 0, sizeof(HAL_ControlWord));
107 return FRC_NetworkCommunication_getControlWord(
108 reinterpret_cast<ControlWord_t*>(controlWord));
109}
110
111static int32_t HAL_GetMatchInfoInternal(HAL_MatchInfo* info) {
112 MatchType_t matchType = MatchType_t::kMatchType_none;
113 int status = FRC_NetworkCommunication_getMatchInfo(
114 info->eventName, &matchType, &info->matchNumber, &info->replayNumber,
115 info->gameSpecificMessage, &info->gameSpecificMessageSize);
116
117 info->matchType = static_cast<HAL_MatchType>(matchType);
118
119 *(std::end(info->eventName) - 1) = '\0';
120
121 return status;
122}
123
124static wpi::mutex* newDSDataAvailableMutex;
125static wpi::condition_variable* newDSDataAvailableCond;
Austin Schuh1e69f942020-11-14 15:06:14 -0800126static int newDSDataAvailableCounter{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800127
Austin Schuh812d0d12021-11-04 20:16:48 -0700128namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -0800129void InitializeFRCDriverStation() {
130 static wpi::mutex newMutex;
131 newDSDataAvailableMutex = &newMutex;
132 static wpi::condition_variable newCond;
133 newDSDataAvailableCond = &newCond;
134}
Austin Schuh812d0d12021-11-04 20:16:48 -0700135} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -0800136
137extern "C" {
138
139int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
140 const char* details, const char* location,
141 const char* callStack, HAL_Bool printMsg) {
142 // Avoid flooding console by keeping track of previous 5 error
143 // messages and only printing again if they're longer than 1 second old.
144 static constexpr int KEEP_MSGS = 5;
145 std::scoped_lock lock(msgMutex);
146 static std::string prevMsg[KEEP_MSGS];
147 static std::chrono::time_point<std::chrono::steady_clock>
148 prevMsgTime[KEEP_MSGS];
149 static bool initialized = false;
150 if (!initialized) {
151 for (int i = 0; i < KEEP_MSGS; i++) {
152 prevMsgTime[i] =
153 std::chrono::steady_clock::now() - std::chrono::seconds(2);
154 }
155 initialized = true;
156 }
157
158 auto curTime = std::chrono::steady_clock::now();
159 int i;
160 for (i = 0; i < KEEP_MSGS; ++i) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700161 if (prevMsg[i] == details) {
162 break;
163 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800164 }
165 int retval = 0;
166 if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700167 std::string_view detailsRef{details};
168 std::string_view locationRef{location};
169 std::string_view callStackRef{callStack};
Brian Silverman8fce7482020-01-05 13:18:21 -0800170
171 // 1 tag, 4 timestamp, 2 seqnum
172 // 2 numOccur, 4 error code, 1 flags, 6 strlen
173 // 1 extra needed for padding on Netcomm end.
174 size_t baseLength = 21;
175
176 if (baseLength + detailsRef.size() + locationRef.size() +
177 callStackRef.size() <=
Austin Schuh1e69f942020-11-14 15:06:14 -0800178 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800179 // Pass through
180 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
181 details, location, callStack);
Austin Schuh1e69f942020-11-14 15:06:14 -0800182 } else if (baseLength + detailsRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800183 // Details too long, cut both location and stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800184 auto newLen = 65535 - baseLength;
Brian Silverman8fce7482020-01-05 13:18:21 -0800185 std::string newDetails{details, newLen};
186 char empty = '\0';
187 retval = FRC_NetworkCommunication_sendError(
188 isError, errorCode, isLVCode, newDetails.c_str(), &empty, &empty);
Austin Schuh1e69f942020-11-14 15:06:14 -0800189 } else if (baseLength + detailsRef.size() + locationRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800190 // Location too long, cut stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800191 auto newLen = 65535 - baseLength - detailsRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800192 std::string newLocation{location, newLen};
193 char empty = '\0';
194 retval = FRC_NetworkCommunication_sendError(
195 isError, errorCode, isLVCode, details, newLocation.c_str(), &empty);
196 } else {
197 // Stack too long
Austin Schuh1e69f942020-11-14 15:06:14 -0800198 auto newLen = 65535 - baseLength - detailsRef.size() - locationRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800199 std::string newCallStack{callStack, newLen};
200 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
201 details, location,
202 newCallStack.c_str());
203 }
204 if (printMsg) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700205 fmt::memory_buffer buf;
Brian Silverman8fce7482020-01-05 13:18:21 -0800206 if (location && location[0] != '\0') {
Austin Schuh812d0d12021-11-04 20:16:48 -0700207 fmt::format_to(fmt::appender{buf},
208 "{} at {}: ", isError ? "Error" : "Warning", location);
Brian Silverman8fce7482020-01-05 13:18:21 -0800209 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700210 fmt::format_to(fmt::appender{buf}, "{}\n", details);
Brian Silverman8fce7482020-01-05 13:18:21 -0800211 if (callStack && callStack[0] != '\0') {
Austin Schuh812d0d12021-11-04 20:16:48 -0700212 fmt::format_to(fmt::appender{buf}, "{}\n", callStack);
Brian Silverman8fce7482020-01-05 13:18:21 -0800213 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700214 std::fwrite(buf.data(), buf.size(), 1, stderr);
Brian Silverman8fce7482020-01-05 13:18:21 -0800215 }
216 if (i == KEEP_MSGS) {
217 // replace the oldest one
218 i = 0;
219 auto first = prevMsgTime[0];
220 for (int j = 1; j < KEEP_MSGS; ++j) {
221 if (prevMsgTime[j] < first) {
222 first = prevMsgTime[j];
223 i = j;
224 }
225 }
226 prevMsg[i] = details;
227 }
228 prevMsgTime[i] = curTime;
229 }
230 return retval;
231}
232
Austin Schuh1e69f942020-11-14 15:06:14 -0800233int32_t HAL_SendConsoleLine(const char* line) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700234 std::string_view lineRef{line};
Austin Schuh1e69f942020-11-14 15:06:14 -0800235 if (lineRef.size() <= 65535) {
236 // Send directly
237 return FRC_NetworkCommunication_sendConsoleLine(line);
238 } else {
239 // Need to truncate
240 std::string newLine{line, 65535};
241 return FRC_NetworkCommunication_sendConsoleLine(newLine.c_str());
242 }
243}
244
Brian Silverman8fce7482020-01-05 13:18:21 -0800245int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
246 return HAL_GetControlWordInternal(controlWord);
247}
248
249int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
250 return HAL_GetJoystickAxesInternal(joystickNum, axes);
251}
252
253int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
254 return HAL_GetJoystickPOVsInternal(joystickNum, povs);
255}
256
257int32_t HAL_GetJoystickButtons(int32_t joystickNum,
258 HAL_JoystickButtons* buttons) {
259 return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
260}
261
262int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
263 HAL_JoystickDescriptor* desc) {
264 return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
265}
266
267int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
268 return HAL_GetMatchInfoInternal(info);
269}
270
271HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
272 HAL_AllianceStationID allianceStation;
273 *status = FRC_NetworkCommunication_getAllianceStation(
274 reinterpret_cast<AllianceStationID_t*>(&allianceStation));
275 return allianceStation;
276}
277
278HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
279 HAL_JoystickDescriptor joystickDesc;
280 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
281 return 0;
282 } else {
283 return joystickDesc.isXbox;
284 }
285}
286
287int32_t HAL_GetJoystickType(int32_t joystickNum) {
288 HAL_JoystickDescriptor joystickDesc;
289 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
290 return -1;
291 } else {
292 return joystickDesc.type;
293 }
294}
295
296char* HAL_GetJoystickName(int32_t joystickNum) {
297 HAL_JoystickDescriptor joystickDesc;
298 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
299 char* name = static_cast<char*>(std::malloc(1));
300 name[0] = '\0';
301 return name;
302 } else {
Austin Schuh812d0d12021-11-04 20:16:48 -0700303 const size_t len = std::strlen(joystickDesc.name) + 1;
304 char* name = static_cast<char*>(std::malloc(len));
305 std::memcpy(name, joystickDesc.name, len);
Brian Silverman8fce7482020-01-05 13:18:21 -0800306 return name;
307 }
308}
309
Austin Schuh812d0d12021-11-04 20:16:48 -0700310void HAL_FreeJoystickName(char* name) {
311 std::free(name);
312}
Brian Silverman8fce7482020-01-05 13:18:21 -0800313
314int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
315 HAL_JoystickDescriptor joystickDesc;
316 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
317 return -1;
318 } else {
319 return joystickDesc.axisTypes[axis];
320 }
321}
322
323int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
324 int32_t leftRumble, int32_t rightRumble) {
325 return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs,
326 leftRumble, rightRumble);
327}
328
329double HAL_GetMatchTime(int32_t* status) {
330 float matchTime;
331 *status = FRC_NetworkCommunication_getMatchTime(&matchTime);
332 return matchTime;
333}
334
335void HAL_ObserveUserProgramStarting(void) {
336 FRC_NetworkCommunication_observeUserProgramStarting();
337}
338
339void HAL_ObserveUserProgramDisabled(void) {
340 FRC_NetworkCommunication_observeUserProgramDisabled();
341}
342
343void HAL_ObserveUserProgramAutonomous(void) {
344 FRC_NetworkCommunication_observeUserProgramAutonomous();
345}
346
347void HAL_ObserveUserProgramTeleop(void) {
348 FRC_NetworkCommunication_observeUserProgramTeleop();
349}
350
351void HAL_ObserveUserProgramTest(void) {
352 FRC_NetworkCommunication_observeUserProgramTest();
353}
354
355static int& GetThreadLocalLastCount() {
356 // There is a rollover error condition here. At Packet# = n * (uintmax), this
357 // will return false when instead it should return true. However, this at a
358 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
359 // worth the cycles to check.
Austin Schuh1e69f942020-11-14 15:06:14 -0800360 thread_local int lastCount{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800361 return lastCount;
362}
363
Brian Silverman8fce7482020-01-05 13:18:21 -0800364HAL_Bool HAL_IsNewControlData(void) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800365 std::scoped_lock lock{*newDSDataAvailableMutex};
Brian Silverman8fce7482020-01-05 13:18:21 -0800366 int& lastCount = GetThreadLocalLastCount();
Austin Schuh1e69f942020-11-14 15:06:14 -0800367 int currentCount = newDSDataAvailableCounter;
Austin Schuh812d0d12021-11-04 20:16:48 -0700368 if (lastCount == currentCount) {
369 return false;
370 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800371 lastCount = currentCount;
372 return true;
373}
374
Austin Schuh812d0d12021-11-04 20:16:48 -0700375void HAL_WaitForDSData(void) {
376 HAL_WaitForDSDataTimeout(0);
377}
Brian Silverman8fce7482020-01-05 13:18:21 -0800378
Brian Silverman8fce7482020-01-05 13:18:21 -0800379HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800380 std::unique_lock lock{*newDSDataAvailableMutex};
381 int& lastCount = GetThreadLocalLastCount();
382 int currentCount = newDSDataAvailableCounter;
383 if (lastCount != currentCount) {
384 lastCount = currentCount;
385 return true;
386 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800387 auto timeoutTime =
388 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
389
Austin Schuh1e69f942020-11-14 15:06:14 -0800390 while (newDSDataAvailableCounter == currentCount) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800391 if (timeout > 0) {
392 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
393 if (timedOut == std::cv_status::timeout) {
394 return false;
395 }
396 } else {
397 newDSDataAvailableCond->wait(lock);
398 }
399 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800400 lastCount = newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800401 return true;
402}
403
404// Constant number to be used for our occur handle
405constexpr int32_t refNumber = 42;
406
407static void newDataOccur(uint32_t refNum) {
408 // Since we could get other values, require our specific handle
409 // to signal our threads
Austin Schuh812d0d12021-11-04 20:16:48 -0700410 if (refNum != refNumber) {
411 return;
412 }
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
Austin Schuh812d0d12021-11-04 20:16:48 -0700428 if (initialized) {
429 return;
430 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800431
432 std::scoped_lock lock(initializeMutex);
433 // Second check in case another thread was waiting
Austin Schuh812d0d12021-11-04 20:16:48 -0700434 if (initialized) {
435 return;
436 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800437
438 // Set up the occur function internally with NetComm
439 NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
440 // Set up our occur reference number
441 setNewDataOccurRef(refNumber);
442
443 initialized = true;
444}
445
446/*
447 * Releases the DS Mutex to allow proper shutdown of any threads that are
448 * waiting on it.
449 */
Austin Schuh812d0d12021-11-04 20:16:48 -0700450void HAL_ReleaseDSMutex(void) {
451 newDataOccur(refNumber);
452}
Brian Silverman8fce7482020-01-05 13:18:21 -0800453
454} // extern "C"