blob: aef85562c2638b843b726691a99ab62fe61f28de [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2017-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -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 "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>
Austin Schuh1e69f942020-11-14 15:06:14 -080021#include <wpi/raw_ostream.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080022
23#include "HALInitializer.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080024#include "hal/cpp/fpga_clock.h"
25#include "hal/simulation/MockHooks.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080026#include "mockdata/DriverStationDataInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080027
28static wpi::mutex msgMutex;
29static wpi::condition_variable* newDSDataAvailableCond;
30static wpi::mutex newDSDataAvailableMutex;
31static int newDSDataAvailableCounter{0};
32static std::atomic_bool isFinalized{false};
33static std::atomic<HALSIM_SendErrorHandler> sendErrorHandler{nullptr};
Austin Schuh1e69f942020-11-14 15:06:14 -080034static std::atomic<HALSIM_SendConsoleLineHandler> sendConsoleLineHandler{
35 nullptr};
Brian Silverman8fce7482020-01-05 13:18:21 -080036
37namespace hal {
38namespace init {
39void InitializeDriverStation() {
40 static wpi::condition_variable nddaC;
41 newDSDataAvailableCond = &nddaC;
42}
43} // namespace init
44} // namespace hal
45
46using namespace hal;
47
48extern "C" {
49
50void HALSIM_SetSendError(HALSIM_SendErrorHandler handler) {
51 sendErrorHandler.store(handler);
52}
53
Austin Schuh1e69f942020-11-14 15:06:14 -080054void HALSIM_SetSendConsoleLine(HALSIM_SendConsoleLineHandler handler) {
55 sendConsoleLineHandler.store(handler);
56}
57
Brian Silverman8fce7482020-01-05 13:18:21 -080058int32_t HAL_SendError(HAL_Bool isError, int32_t errorCode, HAL_Bool isLVCode,
59 const char* details, const char* location,
60 const char* callStack, HAL_Bool printMsg) {
61 auto errorHandler = sendErrorHandler.load();
62 if (errorHandler)
63 return errorHandler(isError, errorCode, isLVCode, details, location,
64 callStack, printMsg);
65 // Avoid flooding console by keeping track of previous 5 error
66 // messages and only printing again if they're longer than 1 second old.
67 static constexpr int KEEP_MSGS = 5;
68 std::scoped_lock lock(msgMutex);
69 static std::string prevMsg[KEEP_MSGS];
Austin Schuh1e69f942020-11-14 15:06:14 -080070 static fpga_clock::time_point prevMsgTime[KEEP_MSGS];
Brian Silverman8fce7482020-01-05 13:18:21 -080071 static bool initialized = false;
72 if (!initialized) {
73 for (int i = 0; i < KEEP_MSGS; i++) {
Austin Schuh1e69f942020-11-14 15:06:14 -080074 prevMsgTime[i] = fpga_clock::now() - std::chrono::seconds(2);
Brian Silverman8fce7482020-01-05 13:18:21 -080075 }
76 initialized = true;
77 }
78
Austin Schuh1e69f942020-11-14 15:06:14 -080079 auto curTime = fpga_clock::now();
Brian Silverman8fce7482020-01-05 13:18:21 -080080 int i;
81 for (i = 0; i < KEEP_MSGS; ++i) {
82 if (prevMsg[i] == details) break;
83 }
84 int retval = 0;
85 if (i == KEEP_MSGS || (curTime - prevMsgTime[i]) >= std::chrono::seconds(1)) {
86 printMsg = true;
87 if (printMsg) {
88 if (location && location[0] != '\0') {
89 std::fprintf(stderr, "%s at %s: ", isError ? "Error" : "Warning",
90 location);
91 }
92 std::fprintf(stderr, "%s\n", details);
93 if (callStack && callStack[0] != '\0') {
94 std::fprintf(stderr, "%s\n", callStack);
95 }
96 }
97 if (i == KEEP_MSGS) {
98 // replace the oldest one
99 i = 0;
100 auto first = prevMsgTime[0];
101 for (int j = 1; j < KEEP_MSGS; ++j) {
102 if (prevMsgTime[j] < first) {
103 first = prevMsgTime[j];
104 i = j;
105 }
106 }
107 prevMsg[i] = details;
108 }
109 prevMsgTime[i] = curTime;
110 }
111 return retval;
112}
113
Austin Schuh1e69f942020-11-14 15:06:14 -0800114int32_t HAL_SendConsoleLine(const char* line) {
115 auto handler = sendConsoleLineHandler.load();
116 if (handler) {
117 return handler(line);
118 }
119 wpi::outs() << line << "\n";
120 wpi::outs().flush();
121 return 0;
122}
123
Brian Silverman8fce7482020-01-05 13:18:21 -0800124int32_t HAL_GetControlWord(HAL_ControlWord* controlWord) {
125 controlWord->enabled = SimDriverStationData->enabled;
126 controlWord->autonomous = SimDriverStationData->autonomous;
127 controlWord->test = SimDriverStationData->test;
128 controlWord->eStop = SimDriverStationData->eStop;
129 controlWord->fmsAttached = SimDriverStationData->fmsAttached;
130 controlWord->dsAttached = SimDriverStationData->dsAttached;
131 return 0;
132}
133
134HAL_AllianceStationID HAL_GetAllianceStation(int32_t* status) {
135 *status = 0;
136 return SimDriverStationData->allianceStationId;
137}
138
139int32_t HAL_GetJoystickAxes(int32_t joystickNum, HAL_JoystickAxes* axes) {
140 SimDriverStationData->GetJoystickAxes(joystickNum, axes);
141 return 0;
142}
143
144int32_t HAL_GetJoystickPOVs(int32_t joystickNum, HAL_JoystickPOVs* povs) {
145 SimDriverStationData->GetJoystickPOVs(joystickNum, povs);
146 return 0;
147}
148
149int32_t HAL_GetJoystickButtons(int32_t joystickNum,
150 HAL_JoystickButtons* buttons) {
151 SimDriverStationData->GetJoystickButtons(joystickNum, buttons);
152 return 0;
153}
154
155int32_t HAL_GetJoystickDescriptor(int32_t joystickNum,
156 HAL_JoystickDescriptor* desc) {
157 SimDriverStationData->GetJoystickDescriptor(joystickNum, desc);
158 return 0;
159}
160
161HAL_Bool HAL_GetJoystickIsXbox(int32_t joystickNum) {
162 HAL_JoystickDescriptor desc;
163 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
164 return desc.isXbox;
165}
166
167int32_t HAL_GetJoystickType(int32_t joystickNum) {
168 HAL_JoystickDescriptor desc;
169 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
170 return desc.type;
171}
172
173char* HAL_GetJoystickName(int32_t joystickNum) {
174 HAL_JoystickDescriptor desc;
175 SimDriverStationData->GetJoystickDescriptor(joystickNum, &desc);
176 size_t len = std::strlen(desc.name);
177 char* name = static_cast<char*>(std::malloc(len + 1));
178 std::memcpy(name, desc.name, len + 1);
179 return name;
180}
181
182void HAL_FreeJoystickName(char* name) { std::free(name); }
183
184int32_t HAL_GetJoystickAxisType(int32_t joystickNum, int32_t axis) { return 0; }
185
186int32_t HAL_SetJoystickOutputs(int32_t joystickNum, int64_t outputs,
187 int32_t leftRumble, int32_t rightRumble) {
188 SimDriverStationData->SetJoystickOutputs(joystickNum, outputs, leftRumble,
189 rightRumble);
190 return 0;
191}
192
193double HAL_GetMatchTime(int32_t* status) {
194 return SimDriverStationData->matchTime;
195}
196
197int32_t HAL_GetMatchInfo(HAL_MatchInfo* info) {
198 SimDriverStationData->GetMatchInfo(info);
199 return 0;
200}
201
202void HAL_ObserveUserProgramStarting(void) { HALSIM_SetProgramStarted(); }
203
204void HAL_ObserveUserProgramDisabled(void) {
205 // TODO
206}
207
208void HAL_ObserveUserProgramAutonomous(void) {
209 // TODO
210}
211
212void HAL_ObserveUserProgramTeleop(void) {
213 // TODO
214}
215
216void HAL_ObserveUserProgramTest(void) {
217 // TODO
218}
219
Brian Silverman8fce7482020-01-05 13:18:21 -0800220static int& GetThreadLocalLastCount() {
221 // There is a rollover error condition here. At Packet# = n * (uintmax), this
222 // will return false when instead it should return true. However, this at a
223 // 20ms rate occurs once every 2.7 years of DS connected runtime, so not
224 // worth the cycles to check.
Austin Schuh1e69f942020-11-14 15:06:14 -0800225 thread_local int lastCount{0};
Brian Silverman8fce7482020-01-05 13:18:21 -0800226 return lastCount;
227}
228
Austin Schuh1e69f942020-11-14 15:06:14 -0800229HAL_Bool HAL_IsNewControlData(void) {
230 std::scoped_lock lock(newDSDataAvailableMutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800231 int& lastCount = GetThreadLocalLastCount();
Austin Schuh1e69f942020-11-14 15:06:14 -0800232 int currentCount = newDSDataAvailableCounter;
233 if (lastCount == currentCount) return false;
234 lastCount = currentCount;
235 return true;
236}
237
238void HAL_WaitForDSData(void) { HAL_WaitForDSDataTimeout(0); }
239
240HAL_Bool HAL_WaitForDSDataTimeout(double timeout) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800241 std::unique_lock lock(newDSDataAvailableMutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800242 int& lastCount = GetThreadLocalLastCount();
Brian Silverman8fce7482020-01-05 13:18:21 -0800243 int currentCount = newDSDataAvailableCounter;
244 if (lastCount != currentCount) {
245 lastCount = currentCount;
246 return true;
247 }
248
249 if (isFinalized.load()) {
250 return false;
251 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800252 auto timeoutTime =
253 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
254
255 while (newDSDataAvailableCounter == currentCount) {
256 if (timeout > 0) {
257 auto timedOut = newDSDataAvailableCond->wait_until(lock, timeoutTime);
258 if (timedOut == std::cv_status::timeout) {
259 return false;
260 }
261 } else {
262 newDSDataAvailableCond->wait(lock);
263 }
264 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800265 lastCount = newDSDataAvailableCounter;
Brian Silverman8fce7482020-01-05 13:18:21 -0800266 return true;
267}
268
269// Constant number to be used for our occur handle
270constexpr int32_t refNumber = 42;
271
272static int32_t newDataOccur(uint32_t refNum) {
273 // Since we could get other values, require our specific handle
274 // to signal our threads
275 if (refNum != refNumber) return 0;
Austin Schuh1e69f942020-11-14 15:06:14 -0800276 SimDriverStationData->CallNewDataCallbacks();
Brian Silverman8fce7482020-01-05 13:18:21 -0800277 std::scoped_lock lock(newDSDataAvailableMutex);
278 // Nofify all threads
279 newDSDataAvailableCounter++;
280 newDSDataAvailableCond->notify_all();
281 return 0;
282}
283
284void HAL_InitializeDriverStation(void) {
285 hal::init::CheckInit();
286 static std::atomic_bool initialized{false};
287 static wpi::mutex initializeMutex;
288 // Initial check, as if it's true initialization has finished
289 if (initialized) return;
290
291 std::scoped_lock lock(initializeMutex);
292 // Second check in case another thread was waiting
293 if (initialized) return;
294
295 SimDriverStationData->ResetData();
296
297 std::atexit([]() {
298 isFinalized.store(true);
299 HAL_ReleaseDSMutex();
300 });
301
302 initialized = true;
303}
304
305void HAL_ReleaseDSMutex(void) { newDataOccur(refNumber); }
306
307} // extern "C"