blob: 0c9c028405c20b8622018ac169b6eed99d2fc0f6 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2017-2018 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 "hal/DriverStation.h"
9
10#ifdef __APPLE__
11#include <pthread.h>
12#endif
13
14#include <cstdio>
15#include <cstdlib>
16#include <cstring>
17#include <string>
18
19#include <wpi/condition_variable.h>
20#include <wpi/mutex.h>
21
22#include "HALInitializer.h"
23#include "mockdata/DriverStationDataInternal.h"
24#include "mockdata/MockHooks.h"
25
26static wpi::mutex msgMutex;
27static wpi::condition_variable* newDSDataAvailableCond;
28static wpi::mutex newDSDataAvailableMutex;
29static int newDSDataAvailableCounter{0};
30static std::atomic_bool isFinalized{false};
31
32namespace hal {
33namespace init {
34void InitializeDriverStation() {
35 static wpi::condition_variable nddaC;
36 newDSDataAvailableCond = &nddaC;
37}
38} // namespace init
39} // namespace hal
40
41using namespace hal;
42
43extern "C" {
44int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
45 const char* details, const char* location,
46 const char* callStack, HAL_Bool printMsg) {
47 // Avoid flooding console by keeping track of previous 5 error
48 // messages and only printing again if they're longer than 1 second old.
49 static constexpr int KEEP_MSGS = 5;
50 std::lock_guard<wpi::mutex> lock(msgMutex);
51 static std::string prevMsg[KEEP_MSGS];
52 static std::chrono::time_point<std::chrono::steady_clock>
53 prevMsgTime[KEEP_MSGS];
54 static bool initialized = false;
55 if (!initialized) {
56 for (int i = 0; i < KEEP_MSGS; i++) {
57 prevMsgTime[i] =
58 std::chrono::steady_clock::now() - std::chrono::seconds(2);
59 }
60 initialized = true;
61 }
62
63 auto curTime = std::chrono::steady_clock::now();
64 int i;
65 for (i = 0; i < KEEP_MSGS; ++i) {
66 if (prevMsg[i] == details) break;
67 }
68 int retval = 0;
69 if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
70 printMsg = true;
71 if (printMsg) {
72 if (location && location[0] != '\0') {
73 std::fprintf(stderr, "%s at %s: ", isError ? "Error" : "Warning",
74 location);
75 }
76 std::fprintf(stderr, "%s\n", details);
77 if (callStack && callStack[0] != '\0') {
78 std::fprintf(stderr, "%s\n", callStack);
79 }
80 }
81 if (i == KEEP_MSGS) {
82 // replace the oldest one
83 i = 0;
84 auto first = prevMsgTime[0];
85 for (int j = 1; j < KEEP_MSGS; ++j) {
86 if (prevMsgTime[j] < first) {
87 first = prevMsgTime[j];
88 i = j;
89 }
90 }
91 prevMsg[i] = details;
92 }
93 prevMsgTime[i] = curTime;
94 }
95 return retval;
96}
97
98int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
99 controlWord->enabled = SimDriverStationData->enabled;
100 controlWord->autonomous = SimDriverStationData->autonomous;
101 controlWord->test = SimDriverStationData->test;
102 controlWord->eStop = SimDriverStationData->eStop;
103 controlWord->fmsAttached = SimDriverStationData->fmsAttached;
104 controlWord->dsAttached = SimDriverStationData->dsAttached;
105 return 0;
106}
107
108HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
109 *status = 0;
110 return SimDriverStationData->allianceStationId;
111}
112
113int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
114 SimDriverStationData->GetJoystickAxes(joystickNum, axes);
115 return 0;
116}
117
118int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
119 SimDriverStationData->GetJoystickPOVs(joystickNum, povs);
120 return 0;
121}
122
123int32_t HAL_GetJoystickButtons(int32_t joystickNum,
124 HAL_JoystickButtons* buttons) {
125 SimDriverStationData->GetJoystickButtons(joystickNum, buttons);
126 return 0;
127}
128
129int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
130 HAL_JoystickDescriptor* desc) {
131 SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
132 return 0;
133}
134
135HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
136 HAL_JoystickDescriptor desc;
137 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
138 return desc.isXbox;
139}
140
141int32_t HAL_GetJoystickType(int32_t joystickNum) {
142 HAL_JoystickDescriptor desc;
143 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
144 return desc.type;
145}
146
147char* HAL_GetJoystickName(int32_t joystickNum) {
148 HAL_JoystickDescriptor desc;
149 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
150 size_t len = std::strlen(desc.name);
151 char* name = static_cast<char*>(std::malloc(len + 1));
152 std::memcpy(name, desc.name, len + 1);
153 return name;
154}
155
156void HAL_FreeJoystickName(char* name) { std::free(name); }
157
158int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) { return 0; }
159
160int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
161 int32_t leftRumble, int32_t rightRumble) {
162 SimDriverStationData->SetJoystickOutputs(joystickNum, outputs, leftRumble,
163 rightRumble);
164 return 0;
165}
166
167double HAL_GetMatchTime(int32_t* status) {
168 return SimDriverStationData->matchTime;
169}
170
171int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
172 SimDriverStationData->GetMatchInfo(info);
173 return 0;
174}
175
176void HAL_ObserveUserProgramStarting(void) { HALSIM_SetProgramStarted(); }
177
178void HAL_ObserveUserProgramDisabled(void) {
179 // TODO
180}
181
182void HAL_ObserveUserProgramAutonomous(void) {
183 // TODO
184}
185
186void HAL_ObserveUserProgramTeleop(void) {
187 // TODO
188}
189
190void HAL_ObserveUserProgramTest(void) {
191 // TODO
192}
193
194#ifdef __APPLE__
195static pthread_key_t lastCountKey;
196static pthread_once_t lastCountKeyOnce = PTHREAD_ONCE_INIT;
197
198static void InitLastCountKey(void) {
199 pthread_key_create(&lastCountKey, std::free);
200}
201#endif
202
203HAL_Bool HAL_IsNewControlData(void) {
204#ifdef __APPLE__
205 pthread_once(&lastCountKeyOnce, InitLastCountKey);
206 int* lastCountPtr = static_cast<int*>(pthread_getspecific(lastCountKey));
207 if (!lastCountPtr) {
208 lastCountPtr = static_cast<int*>(std::malloc(sizeof(int)));
209 *lastCountPtr = -1;
210 pthread_setspecific(lastCountKey, lastCountPtr);
211 }
212 int& lastCount = *lastCountPtr;
213#else
214 thread_local int lastCount{-1};
215#endif
216 // There is a rollover error condition here. At Packet# = n * (uintmax), this
217 // will return false when instead it should return true. However, this at a
218 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
219 // worth the cycles to check.
220 int currentCount = 0;
221 {
222 std::unique_lock<wpi::mutex> lock(newDSDataAvailableMutex);
223 currentCount = newDSDataAvailableCounter;
224 }
225 if (lastCount == currentCount) return false;
226 lastCount = currentCount;
227 return true;
228}
229
230void HAL_WaitForDSData(void) { HAL_WaitForDSDataTimeout(0); }
231
232HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
233 if (isFinalized.load()) {
234 return false;
235 }
236 auto timeoutTime =
237 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
238
239 std::unique_lock<wpi::mutex> lock(newDSDataAvailableMutex);
240 int currentCount = newDSDataAvailableCounter;
241 while (newDSDataAvailableCounter == currentCount) {
242 if (timeout > 0) {
243 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
244 if (timedOut == std::cv_status::timeout) {
245 return false;
246 }
247 } else {
248 newDSDataAvailableCond->wait(lock);
249 }
250 }
251 return true;
252}
253
254// Constant number to be used for our occur handle
255constexpr int32_t refNumber = 42;
256
257static int32_t newDataOccur(uint32_t refNum) {
258 // Since we could get other values, require our specific handle
259 // to signal our threads
260 if (refNum != refNumber) return 0;
261 std::lock_guard<wpi::mutex> lock(newDSDataAvailableMutex);
262 // Nofify all threads
263 newDSDataAvailableCounter++;
264 newDSDataAvailableCond->notify_all();
265 return 0;
266}
267
268void HAL_InitializeDriverStation(void) {
269 hal::init::CheckInit();
270 static std::atomic_bool initialized{false};
271 static wpi::mutex initializeMutex;
272 // Initial check, as if it's true initialization has finished
273 if (initialized) return;
274
275 std::lock_guard<wpi::mutex> lock(initializeMutex);
276 // Second check in case another thread was waiting
277 if (initialized) return;
278
279 SimDriverStationData->ResetData();
280
281 std::atexit([]() {
282 isFinalized.store(true);
283 HAL_ReleaseDSMutex();
284 });
285
286 initialized = true;
287}
288
289void HAL_ReleaseDSMutex(void) { newDataOccur(refNumber); }
290
291} // extern "C"