blob: 23c874f7417db4a98dd778ed3becf06846938657 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -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
Brian Silverman41cdd3e2019-01-19 19:48:58 -080033// Message and Data variables
34static wpi::mutex msgMutex;
35
Brian Silverman41cdd3e2019-01-19 19:48:58 -080036static 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;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080086 desc->type = (std::numeric_limits<uint8_t>::max)();
Brian Silverman41cdd3e2019-01-19 19:48:58 -080087 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
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800125static wpi::mutex* newDSDataAvailableMutex;
126static wpi::condition_variable* newDSDataAvailableCond;
127static int newDSDataAvailableCounter{0};
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800128
129namespace hal {
130namespace init {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800131void InitializeFRCDriverStation() {
132 static wpi::mutex newMutex;
133 newDSDataAvailableMutex = &newMutex;
134 static wpi::condition_variable newCond;
135 newDSDataAvailableCond = &newCond;
136}
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800137} // 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;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800148 std::scoped_lock lock(msgMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800149 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)) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800168 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 }
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800205 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) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800233 return HAL_GetControlWordInternal(controlWord);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800234}
235
236int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800237 return HAL_GetJoystickAxesInternal(joystickNum, axes);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800238}
239
240int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800241 return HAL_GetJoystickPOVsInternal(joystickNum, povs);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800242}
243
244int32_t HAL_GetJoystickButtons(int32_t joystickNum,
245 HAL_JoystickButtons* buttons) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800246 return HAL_GetJoystickButtonsInternal(joystickNum, buttons);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800247}
248
249int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
250 HAL_JoystickDescriptor* desc) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800251 return HAL_GetJoystickDescriptorInternal(joystickNum, desc);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800252}
253
254int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800255 return HAL_GetMatchInfoInternal(info);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800256}
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
341HAL_Bool HAL_IsNewControlData(void) {
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};
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800347 std::lock_guard lock{*newDSDataAvailableMutex};
348 int currentCount = newDSDataAvailableCounter;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800349 if (lastCount == currentCount) return false;
350 lastCount = currentCount;
351 return true;
352}
353
354/**
355 * Waits for the newest DS packet to arrive. Note that this is a blocking call.
356 */
357void HAL_WaitForDSData(void) { HAL_WaitForDSDataTimeout(0); }
358
359/**
360 * Waits for the newest DS packet to arrive. If timeout is <= 0, this will wait
361 * forever. Otherwise, it will wait until either a new packet, or the timeout
362 * time has passed. Returns true on new data, false on timeout.
363 */
364HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
365 auto timeoutTime =
366 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
367
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800368 std::unique_lock lock{*newDSDataAvailableMutex};
369 int currentCount = newDSDataAvailableCounter;
370 while (newDSDataAvailableCounter == currentCount) {
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800371 if (timeout > 0) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800372 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800373 if (timedOut == std::cv_status::timeout) {
374 return false;
375 }
376 } else {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800377 newDSDataAvailableCond->wait(lock);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800378 }
379 }
380 return true;
381}
382
383// Constant number to be used for our occur handle
384constexpr int32_t refNumber = 42;
385
386static void newDataOccur(uint32_t refNum) {
387 // Since we could get other values, require our specific handle
388 // to signal our threads
389 if (refNum != refNumber) return;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800390 // Notify all threads
391 newDSDataAvailableCounter++;
392 newDSDataAvailableCond->notify_all();
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800393}
394
395/*
396 * Call this to initialize the driver station communication. This will properly
397 * handle multiple calls. However note that this CANNOT be called from a library
398 * that interfaces with LabVIEW.
399 */
400void HAL_InitializeDriverStation(void) {
401 static std::atomic_bool initialized{false};
402 static wpi::mutex initializeMutex;
403 // Initial check, as if it's true initialization has finished
404 if (initialized) return;
405
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800406 std::scoped_lock lock(initializeMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800407 // Second check in case another thread was waiting
408 if (initialized) return;
409
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800410 // Set up the occur function internally with NetComm
411 NetCommRPCProxy_SetOccurFuncPointer(newDataOccur);
412 // Set up our occur reference number
413 setNewDataOccurRef(refNumber);
414
415 initialized = true;
416}
417
418/*
419 * Releases the DS Mutex to allow proper shutdown of any threads that are
420 * waiting on it.
421 */
422void HAL_ReleaseDSMutex(void) { newDataOccur(refNumber); }
423
424} // extern "C"