blob: e7ef1948f904efac855ccaa08db5e7149f23a287 [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;
Austin Schuh75263e32022-02-22 18:05:32 -0800113 info->gameSpecificMessageSize = sizeof(info->gameSpecificMessage);
Brian Silverman8fce7482020-01-05 13:18:21 -0800114 int status = FRC_NetworkCommunication_getMatchInfo(
115 info->eventName, &matchType, &info->matchNumber, &info->replayNumber,
116 info->gameSpecificMessage, &info->gameSpecificMessageSize);
117
Austin Schuh75263e32022-02-22 18:05:32 -0800118 if (info->gameSpecificMessageSize > sizeof(info->gameSpecificMessage)) {
119 info->gameSpecificMessageSize = 0;
120 }
121
Brian Silverman8fce7482020-01-05 13:18:21 -0800122 info->matchType = static_cast<HAL_MatchType>(matchType);
123
124 *(std::end(info->eventName) - 1) = '\0';
125
126 return status;
127}
128
129static wpi::mutex* newDSDataAvailableMutex;
130static wpi::condition_variable* newDSDataAvailableCond;
Austin Schuh1e69f942020-11-14 15:06:14 -0800131static int newDSDataAvailableCounter{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800132
Austin Schuh812d0d12021-11-04 20:16:48 -0700133namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -0800134void InitializeFRCDriverStation() {
135 static wpi::mutex newMutex;
136 newDSDataAvailableMutex = &newMutex;
137 static wpi::condition_variable newCond;
138 newDSDataAvailableCond = &newCond;
139}
Austin Schuh812d0d12021-11-04 20:16:48 -0700140} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -0800141
142extern "C" {
143
144int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
145 const char* details, const char* location,
146 const char* callStack, HAL_Bool printMsg) {
147 // Avoid flooding console by keeping track of previous 5 error
148 // messages and only printing again if they're longer than 1 second old.
149 static constexpr int KEEP_MSGS = 5;
150 std::scoped_lock lock(msgMutex);
151 static std::string prevMsg[KEEP_MSGS];
152 static std::chrono::time_point<std::chrono::steady_clock>
153 prevMsgTime[KEEP_MSGS];
154 static bool initialized = false;
155 if (!initialized) {
156 for (int i = 0; i < KEEP_MSGS; i++) {
157 prevMsgTime[i] =
158 std::chrono::steady_clock::now() - std::chrono::seconds(2);
159 }
160 initialized = true;
161 }
162
163 auto curTime = std::chrono::steady_clock::now();
164 int i;
165 for (i = 0; i < KEEP_MSGS; ++i) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700166 if (prevMsg[i] == details) {
167 break;
168 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800169 }
170 int retval = 0;
171 if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700172 std::string_view detailsRef{details};
173 std::string_view locationRef{location};
174 std::string_view callStackRef{callStack};
Brian Silverman8fce7482020-01-05 13:18:21 -0800175
176 // 1 tag, 4 timestamp, 2 seqnum
177 // 2 numOccur, 4 error code, 1 flags, 6 strlen
178 // 1 extra needed for padding on Netcomm end.
179 size_t baseLength = 21;
180
181 if (baseLength + detailsRef.size() + locationRef.size() +
182 callStackRef.size() <=
Austin Schuh1e69f942020-11-14 15:06:14 -0800183 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800184 // Pass through
185 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
186 details, location, callStack);
Austin Schuh1e69f942020-11-14 15:06:14 -0800187 } else if (baseLength + detailsRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800188 // Details too long, cut both location and stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800189 auto newLen = 65535 - baseLength;
Brian Silverman8fce7482020-01-05 13:18:21 -0800190 std::string newDetails{details, newLen};
191 char empty = '\0';
192 retval = FRC_NetworkCommunication_sendError(
193 isError, errorCode, isLVCode, newDetails.c_str(), &empty, &empty);
Austin Schuh1e69f942020-11-14 15:06:14 -0800194 } else if (baseLength + detailsRef.size() + locationRef.size() > 65535) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800195 // Location too long, cut stack
Austin Schuh1e69f942020-11-14 15:06:14 -0800196 auto newLen = 65535 - baseLength - detailsRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800197 std::string newLocation{location, newLen};
198 char empty = '\0';
199 retval = FRC_NetworkCommunication_sendError(
200 isError, errorCode, isLVCode, details, newLocation.c_str(), &empty);
201 } else {
202 // Stack too long
Austin Schuh1e69f942020-11-14 15:06:14 -0800203 auto newLen = 65535 - baseLength - detailsRef.size() - locationRef.size();
Brian Silverman8fce7482020-01-05 13:18:21 -0800204 std::string newCallStack{callStack, newLen};
205 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
206 details, location,
207 newCallStack.c_str());
208 }
209 if (printMsg) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700210 fmt::memory_buffer buf;
Brian Silverman8fce7482020-01-05 13:18:21 -0800211 if (location && location[0] != '\0') {
Austin Schuh812d0d12021-11-04 20:16:48 -0700212 fmt::format_to(fmt::appender{buf},
213 "{} at {}: ", isError ? "Error" : "Warning", location);
Brian Silverman8fce7482020-01-05 13:18:21 -0800214 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700215 fmt::format_to(fmt::appender{buf}, "{}\n", details);
Brian Silverman8fce7482020-01-05 13:18:21 -0800216 if (callStack && callStack[0] != '\0') {
Austin Schuh812d0d12021-11-04 20:16:48 -0700217 fmt::format_to(fmt::appender{buf}, "{}\n", callStack);
Brian Silverman8fce7482020-01-05 13:18:21 -0800218 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700219 std::fwrite(buf.data(), buf.size(), 1, stderr);
Brian Silverman8fce7482020-01-05 13:18:21 -0800220 }
221 if (i == KEEP_MSGS) {
222 // replace the oldest one
223 i = 0;
224 auto first = prevMsgTime[0];
225 for (int j = 1; j < KEEP_MSGS; ++j) {
226 if (prevMsgTime[j] < first) {
227 first = prevMsgTime[j];
228 i = j;
229 }
230 }
231 prevMsg[i] = details;
232 }
233 prevMsgTime[i] = curTime;
234 }
235 return retval;
236}
237
Austin Schuh1e69f942020-11-14 15:06:14 -0800238int32_t HAL_SendConsoleLine(const char* line) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700239 std::string_view lineRef{line};
Austin Schuh1e69f942020-11-14 15:06:14 -0800240 if (lineRef.size() <= 65535) {
241 // Send directly
242 return FRC_NetworkCommunication_sendConsoleLine(line);
243 } else {
244 // Need to truncate
245 std::string newLine{line, 65535};
246 return FRC_NetworkCommunication_sendConsoleLine(newLine.c_str());
247 }
248}
249
Brian Silverman8fce7482020-01-05 13:18:21 -0800250int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
251 return HAL_GetControlWordInternal(controlWord);
252}
253
254int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
255 return HAL_GetJoystickAxesInternal(joystickNum, axes);
256}
257
258int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
259 return HAL_GetJoystickPOVsInternal(joystickNum, povs);
260}
261
262int32_t HAL_GetJoystickButtons(int32_t joystickNum,
263 HAL_JoystickButtons* buttons) {
264 return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
265}
266
267int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
268 HAL_JoystickDescriptor* desc) {
269 return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
270}
271
272int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
273 return HAL_GetMatchInfoInternal(info);
274}
275
276HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
277 HAL_AllianceStationID allianceStation;
278 *status = FRC_NetworkCommunication_getAllianceStation(
279 reinterpret_cast<AllianceStationID_t*>(&allianceStation));
280 return allianceStation;
281}
282
283HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
284 HAL_JoystickDescriptor joystickDesc;
285 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
286 return 0;
287 } else {
288 return joystickDesc.isXbox;
289 }
290}
291
292int32_t HAL_GetJoystickType(int32_t joystickNum) {
293 HAL_JoystickDescriptor joystickDesc;
294 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
295 return -1;
296 } else {
297 return joystickDesc.type;
298 }
299}
300
301char* HAL_GetJoystickName(int32_t joystickNum) {
302 HAL_JoystickDescriptor joystickDesc;
303 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
304 char* name = static_cast<char*>(std::malloc(1));
305 name[0] = '\0';
306 return name;
307 } else {
Austin Schuh812d0d12021-11-04 20:16:48 -0700308 const size_t len = std::strlen(joystickDesc.name) + 1;
309 char* name = static_cast<char*>(std::malloc(len));
310 std::memcpy(name, joystickDesc.name, len);
Brian Silverman8fce7482020-01-05 13:18:21 -0800311 return name;
312 }
313}
314
Austin Schuh812d0d12021-11-04 20:16:48 -0700315void HAL_FreeJoystickName(char* name) {
316 std::free(name);
317}
Brian Silverman8fce7482020-01-05 13:18:21 -0800318
319int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
320 HAL_JoystickDescriptor joystickDesc;
321 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
322 return -1;
323 } else {
324 return joystickDesc.axisTypes[axis];
325 }
326}
327
328int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
329 int32_t leftRumble, int32_t rightRumble) {
330 return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs,
331 leftRumble, rightRumble);
332}
333
334double HAL_GetMatchTime(int32_t* status) {
335 float matchTime;
336 *status = FRC_NetworkCommunication_getMatchTime(&matchTime);
337 return matchTime;
338}
339
340void HAL_ObserveUserProgramStarting(void) {
341 FRC_NetworkCommunication_observeUserProgramStarting();
342}
343
344void HAL_ObserveUserProgramDisabled(void) {
345 FRC_NetworkCommunication_observeUserProgramDisabled();
346}
347
348void HAL_ObserveUserProgramAutonomous(void) {
349 FRC_NetworkCommunication_observeUserProgramAutonomous();
350}
351
352void HAL_ObserveUserProgramTeleop(void) {
353 FRC_NetworkCommunication_observeUserProgramTeleop();
354}
355
356void HAL_ObserveUserProgramTest(void) {
357 FRC_NetworkCommunication_observeUserProgramTest();
358}
359
360static int& GetThreadLocalLastCount() {
361 // There is a rollover error condition here. At Packet# = n * (uintmax), this
362 // will return false when instead it should return true. However, this at a
363 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
364 // worth the cycles to check.
Austin Schuh1e69f942020-11-14 15:06:14 -0800365 thread_local int lastCount{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800366 return lastCount;
367}
368
Brian Silverman8fce7482020-01-05 13:18:21 -0800369HAL_Bool HAL_IsNewControlData(void) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800370 std::scoped_lock lock{*newDSDataAvailableMutex};
Brian Silverman8fce7482020-01-05 13:18:21 -0800371 int& lastCount = GetThreadLocalLastCount();
Austin Schuh1e69f942020-11-14 15:06:14 -0800372 int currentCount = newDSDataAvailableCounter;
Austin Schuh812d0d12021-11-04 20:16:48 -0700373 if (lastCount == currentCount) {
374 return false;
375 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800376 lastCount = currentCount;
377 return true;
378}
379
Austin Schuh812d0d12021-11-04 20:16:48 -0700380void HAL_WaitForDSData(void) {
381 HAL_WaitForDSDataTimeout(0);
382}
Brian Silverman8fce7482020-01-05 13:18:21 -0800383
Brian Silverman8fce7482020-01-05 13:18:21 -0800384HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800385 std::unique_lock lock{*newDSDataAvailableMutex};
386 int& lastCount = GetThreadLocalLastCount();
387 int currentCount = newDSDataAvailableCounter;
388 if (lastCount != currentCount) {
389 lastCount = currentCount;
390 return true;
391 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800392 auto timeoutTime =
393 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
394
Austin Schuh1e69f942020-11-14 15:06:14 -0800395 while (newDSDataAvailableCounter == currentCount) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800396 if (timeout > 0) {
397 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
398 if (timedOut == std::cv_status::timeout) {
399 return false;
400 }
401 } else {
402 newDSDataAvailableCond->wait(lock);
403 }
404 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800405 lastCount = newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800406 return true;
407}
408
409// Constant number to be used for our occur handle
410constexpr int32_t refNumber = 42;
411
412static void newDataOccur(uint32_t refNum) {
413 // Since we could get other values, require our specific handle
414 // to signal our threads
Austin Schuh812d0d12021-11-04 20:16:48 -0700415 if (refNum != refNumber) {
416 return;
417 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800418 std::scoped_lock lock{*newDSDataAvailableMutex};
Brian Silverman8fce7482020-01-05 13:18:21 -0800419 // Notify all threads
Austin Schuh1e69f942020-11-14 15:06:14 -0800420 ++newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800421 newDSDataAvailableCond->notify_all();
422}
423
424/*
425 * Call this to initialize the driver station communication. This will properly
426 * handle multiple calls. However note that this CANNOT be called from a library
427 * that interfaces with LabVIEW.
428 */
429void HAL_InitializeDriverStation(void) {
430 static std::atomic_bool initialized{false};
431 static wpi::mutex initializeMutex;
432 // Initial check, as if it's true initialization has finished
Austin Schuh812d0d12021-11-04 20:16:48 -0700433 if (initialized) {
434 return;
435 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800436
437 std::scoped_lock lock(initializeMutex);
438 // Second check in case another thread was waiting
Austin Schuh812d0d12021-11-04 20:16:48 -0700439 if (initialized) {
440 return;
441 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800442
443 // Set up the occur function internally with NetComm
444 NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
445 // Set up our occur reference number
446 setNewDataOccurRef(refNumber);
447
448 initialized = true;
449}
450
451/*
452 * Releases the DS Mutex to allow proper shutdown of any threads that are
453 * waiting on it.
454 */
Austin Schuh812d0d12021-11-04 20:16:48 -0700455void HAL_ReleaseDSMutex(void) {
456 newDataOccur(refNumber);
457}
Brian Silverman8fce7482020-01-05 13:18:21 -0800458
459} // extern "C"