blob: 5b29815b2885c98c504b24458c4a2612a4e4fffe [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2016-2019 FIRST. 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 <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;
127static std::atomic_int newDSDataAvailableCounter{0};
128
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() <=
179 65536) {
180 // Pass through
181 retval = FRC_NetworkCommunication_sendError(isError, errorCode, isLVCode,
182 details, location, callStack);
183 } else if (baseLength + detailsRef.size() > 65536) {
184 // Details too long, cut both location and stack
185 auto newLen = 65536 - baseLength;
186 std::string newDetails{details, newLen};
187 char empty = '\0';
188 retval = FRC_NetworkCommunication_sendError(
189 isError, errorCode, isLVCode, newDetails.c_str(), &empty, &empty);
190 } else if (baseLength + detailsRef.size() + locationRef.size() > 65536) {
191 // Location too long, cut stack
192 auto newLen = 65536 - baseLength - detailsRef.size();
193 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
199 auto newLen = 65536 - baseLength - detailsRef.size() - locationRef.size();
200 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
232int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
233 return HAL_GetControlWordInternal(controlWord);
234}
235
236int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
237 return HAL_GetJoystickAxesInternal(joystickNum, axes);
238}
239
240int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
241 return HAL_GetJoystickPOVsInternal(joystickNum, povs);
242}
243
244int32_t HAL_GetJoystickButtons(int32_t joystickNum,
245 HAL_JoystickButtons* buttons) {
246 return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
247}
248
249int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
250 HAL_JoystickDescriptor* desc) {
251 return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
252}
253
254int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
255 return HAL_GetMatchInfoInternal(info);
256}
257
258HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
259 HAL_AllianceStationID allianceStation;
260 *status = FRC_NetworkCommunication_getAllianceStation(
261 reinterpret_cast<AllianceStationID_t*>(&allianceStation));
262 return allianceStation;
263}
264
265HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
266 HAL_JoystickDescriptor joystickDesc;
267 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
268 return 0;
269 } else {
270 return joystickDesc.isXbox;
271 }
272}
273
274int32_t HAL_GetJoystickType(int32_t joystickNum) {
275 HAL_JoystickDescriptor joystickDesc;
276 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
277 return -1;
278 } else {
279 return joystickDesc.type;
280 }
281}
282
283char* HAL_GetJoystickName(int32_t joystickNum) {
284 HAL_JoystickDescriptor joystickDesc;
285 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
286 char* name = static_cast<char*>(std::malloc(1));
287 name[0] = '\0';
288 return name;
289 } else {
290 size_t len = std::strlen(joystickDesc.name);
291 char* name = static_cast<char*>(std::malloc(len + 1));
292 std::strncpy(name, joystickDesc.name, len);
293 name[len] = '\0';
294 return name;
295 }
296}
297
298void HAL_FreeJoystickName(char* name) { std::free(name); }
299
300int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) {
301 HAL_JoystickDescriptor joystickDesc;
302 if (HAL_GetJoystickDescriptor(joystickNum, &joystickDesc) < 0) {
303 return -1;
304 } else {
305 return joystickDesc.axisTypes[axis];
306 }
307}
308
309int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
310 int32_t leftRumble, int32_t rightRumble) {
311 return FRC_NetworkCommunication_setJoystickOutputs(joystickNum, outputs,
312 leftRumble, rightRumble);
313}
314
315double HAL_GetMatchTime(int32_t* status) {
316 float matchTime;
317 *status = FRC_NetworkCommunication_getMatchTime(&matchTime);
318 return matchTime;
319}
320
321void HAL_ObserveUserProgramStarting(void) {
322 FRC_NetworkCommunication_observeUserProgramStarting();
323}
324
325void HAL_ObserveUserProgramDisabled(void) {
326 FRC_NetworkCommunication_observeUserProgramDisabled();
327}
328
329void HAL_ObserveUserProgramAutonomous(void) {
330 FRC_NetworkCommunication_observeUserProgramAutonomous();
331}
332
333void HAL_ObserveUserProgramTeleop(void) {
334 FRC_NetworkCommunication_observeUserProgramTeleop();
335}
336
337void HAL_ObserveUserProgramTest(void) {
338 FRC_NetworkCommunication_observeUserProgramTest();
339}
340
341static int& GetThreadLocalLastCount() {
342 // There is a rollover error condition here. At Packet# = n * (uintmax), this
343 // will return false when instead it should return true. However, this at a
344 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
345 // worth the cycles to check.
346 thread_local int lastCount{-1};
347 return lastCount;
348}
349
350void HAL_WaitForCachedControlData(void) {
351 HAL_WaitForCachedControlDataTimeout(0);
352}
353
354HAL_Bool HAL_WaitForCachedControlDataTimeout(double timeout) {
355 int& lastCount = GetThreadLocalLastCount();
356 int currentCount = newDSDataAvailableCounter.load();
357 if (lastCount != currentCount) {
358 lastCount = currentCount;
359 return true;
360 }
361 auto timeoutTime =
362 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
363
364 std::unique_lock lock{*newDSDataAvailableMutex};
365 while (newDSDataAvailableCounter.load() == currentCount) {
366 if (timeout > 0) {
367 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
368 if (timedOut == std::cv_status::timeout) {
369 return false;
370 }
371 } else {
372 newDSDataAvailableCond->wait(lock);
373 }
374 }
375 return true;
376}
377
378HAL_Bool HAL_IsNewControlData(void) {
379 int& lastCount = GetThreadLocalLastCount();
380 int currentCount = newDSDataAvailableCounter.load();
381 if (lastCount == currentCount) return false;
382 lastCount = currentCount;
383 return true;
384}
385
386/**
387 * Waits for the newest DS packet to arrive. Note that this is a blocking call.
388 */
389void HAL_WaitForDSData(void) { HAL_WaitForDSDataTimeout(0); }
390
391/**
392 * Waits for the newest DS packet to arrive. If timeout is <= 0, this will wait
393 * forever. Otherwise, it will wait until either a new packet, or the timeout
394 * time has passed. Returns true on new data, false on timeout.
395 */
396HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
397 auto timeoutTime =
398 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
399
400 int currentCount = newDSDataAvailableCounter.load();
401 std::unique_lock lock{*newDSDataAvailableMutex};
402 while (newDSDataAvailableCounter.load() == currentCount) {
403 if (timeout > 0) {
404 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
405 if (timedOut == std::cv_status::timeout) {
406 return false;
407 }
408 } else {
409 newDSDataAvailableCond->wait(lock);
410 }
411 }
412 return true;
413}
414
415// Constant number to be used for our occur handle
416constexpr int32_t refNumber = 42;
417
418static void newDataOccur(uint32_t refNum) {
419 // Since we could get other values, require our specific handle
420 // to signal our threads
421 if (refNum != refNumber) return;
422 // Notify all threads
423 newDSDataAvailableCounter.fetch_add(1);
424 newDSDataAvailableCond->notify_all();
425}
426
427/*
428 * Call this to initialize the driver station communication. This will properly
429 * handle multiple calls. However note that this CANNOT be called from a library
430 * that interfaces with LabVIEW.
431 */
432void HAL_InitializeDriverStation(void) {
433 static std::atomic_bool initialized{false};
434 static wpi::mutex initializeMutex;
435 // Initial check, as if it's true initialization has finished
436 if (initialized) return;
437
438 std::scoped_lock lock(initializeMutex);
439 // Second check in case another thread was waiting
440 if (initialized) return;
441
442 // Set up the occur function internally with NetComm
443 NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
444 // Set up our occur reference number
445 setNewDataOccurRef(refNumber);
446
447 initialized = true;
448}
449
450/*
451 * Releases the DS Mutex to allow proper shutdown of any threads that are
452 * waiting on it.
453 */
454void HAL_ReleaseDSMutex(void) { newDataOccur(refNumber); }
455
456} // extern "C"