blob: 455a9874e52ae630ee7c9a08c035267dd51e66b8 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008. All Rights Reserved.
3 */
4/* Open Source Software - may be modified and shared by FRC teams. The code */
5/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
6/*----------------------------------------------------------------------------*/
7
8#include "DriverStation.h"
9#include "AnalogInput.h"
10#include "Timer.h"
11#include "NetworkCommunication/FRCComm.h"
12#include "MotorSafetyHelper.h"
13#include "Utility.h"
14#include "WPIErrors.h"
15#include <string.h>
16#include "Log.hpp"
17
18// set the logging level
19TLogLevel dsLogLevel = logDEBUG;
20const double JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL = 1.0;
21
22#define DS_LOG(level) \
23 if (level > dsLogLevel) \
24 ; \
25 else \
26 Log().Get(level)
27
28const uint32_t DriverStation::kJoystickPorts;
29
30/**
31 * DriverStation constructor.
32 *
33 * This is only called once the first time GetInstance() is called
34 */
35DriverStation::DriverStation() {
36 // All joysticks should default to having zero axes, povs and buttons, so
37 // uninitialized memory doesn't get sent to speed controllers.
38 for (unsigned int i = 0; i < kJoystickPorts; i++) {
39 m_joystickAxes[i].count = 0;
40 m_joystickPOVs[i].count = 0;
41 m_joystickButtons[i].count = 0;
42 m_joystickDescriptor[i].isXbox = 0;
43 m_joystickDescriptor[i].type = -1;
44 m_joystickDescriptor[i].name[0] = '\0';
45 }
46 // Register that semaphore with the network communications task.
47 // It will signal when new packet data is available.
48 HALSetNewDataSem(&m_packetDataAvailableCond);
49
50 AddToSingletonList();
51
52 m_task = Task("DriverStation", &DriverStation::Run, this);
53}
54
55DriverStation::~DriverStation() {
56 m_isRunning = false;
57 m_task.join();
58
59 // Unregister our semaphore.
60 HALSetNewDataSem(nullptr);
61}
62
63void DriverStation::Run() {
64 m_isRunning = true;
65 int period = 0;
66 while (m_isRunning) {
67 {
68 std::unique_lock<priority_mutex> lock(m_packetDataAvailableMutex);
69 m_packetDataAvailableCond.wait(lock);
70 }
71 GetData();
72 m_waitForDataCond.notify_all();
73
74 if (++period >= 4) {
75 MotorSafetyHelper::CheckMotors();
76 period = 0;
77 }
78 if (m_userInDisabled) HALNetworkCommunicationObserveUserProgramDisabled();
79 if (m_userInAutonomous)
80 HALNetworkCommunicationObserveUserProgramAutonomous();
81 if (m_userInTeleop) HALNetworkCommunicationObserveUserProgramTeleop();
82 if (m_userInTest) HALNetworkCommunicationObserveUserProgramTest();
83 }
84}
85
86/**
87 * Return a pointer to the singleton DriverStation.
88 * @return Pointer to the DS instance
89 */
90DriverStation &DriverStation::GetInstance() {
91 static DriverStation instance;
92 return instance;
93}
94
95/**
96 * Copy data from the DS task for the user.
97 * If no new data exists, it will just be returned, otherwise
98 * the data will be copied from the DS polling loop.
99 */
100void DriverStation::GetData() {
101 // Get the status of all of the joysticks
102 for (uint8_t stick = 0; stick < kJoystickPorts; stick++) {
103 HALGetJoystickAxes(stick, &m_joystickAxes[stick]);
104 HALGetJoystickPOVs(stick, &m_joystickPOVs[stick]);
105 HALGetJoystickButtons(stick, &m_joystickButtons[stick]);
106 HALGetJoystickDescriptor(stick, &m_joystickDescriptor[stick]);
107 }
108 m_newControlData.give();
109}
110
111/**
112 * Read the battery voltage.
113 *
114 * @return The battery voltage in Volts.
115 */
116float DriverStation::GetBatteryVoltage() const {
117 int32_t status = 0;
118 float voltage = getVinVoltage(&status);
119 wpi_setErrorWithContext(status, "getVinVoltage");
120
121 return voltage;
122}
123
124/**
125 * Reports errors related to unplugged joysticks
126 * Throttles the errors so that they don't overwhelm the DS
127 */
128void DriverStation::ReportJoystickUnpluggedError(std::string message) {
129 double currentTime = Timer::GetFPGATimestamp();
130 if (currentTime > m_nextMessageTime) {
131 ReportError(message);
132 m_nextMessageTime = currentTime + JOYSTICK_UNPLUGGED_MESSAGE_INTERVAL;
133 }
134}
135
136/**
137 * Returns the number of axes on a given joystick port
138 *
139 * @param stick The joystick port number
140 * @return The number of axes on the indicated joystick
141 */
142int DriverStation::GetStickAxisCount(uint32_t stick) const {
143 if (stick >= kJoystickPorts) {
144 wpi_setWPIError(BadJoystickIndex);
145 return 0;
146 }
147 HALJoystickAxes joystickAxes;
148 HALGetJoystickAxes(stick, &joystickAxes);
149 return joystickAxes.count;
150}
151
152/**
153 * Returns the name of the joystick at the given port
154 *
155 * @param stick The joystick port number
156 * @return The name of the joystick at the given port
157 */
158std::string DriverStation::GetJoystickName(uint32_t stick) const {
159 if (stick >= kJoystickPorts) {
160 wpi_setWPIError(BadJoystickIndex);
161 }
162 std::string retVal(m_joystickDescriptor[0].name);
163 return retVal;
164}
165
166/**
167 * Returns the type of joystick at a given port
168 *
169 * @param stick The joystick port number
170 * @return The HID type of joystick at the given port
171 */
172int DriverStation::GetJoystickType(uint32_t stick) const {
173 if (stick >= kJoystickPorts) {
174 wpi_setWPIError(BadJoystickIndex);
175 return -1;
176 }
177 return (int)m_joystickDescriptor[stick].type;
178}
179
180/**
181 * Returns a boolean indicating if the controller is an xbox controller.
182 *
183 * @param stick The joystick port number
184 * @return A boolean that is true if the controller is an xbox controller.
185 */
186bool DriverStation::GetJoystickIsXbox(uint32_t stick) const {
187 if (stick >= kJoystickPorts) {
188 wpi_setWPIError(BadJoystickIndex);
189 return false;
190 }
191 return (bool)m_joystickDescriptor[stick].isXbox;
192}
193
194/**
195 * Returns the types of Axes on a given joystick port
196 *
197 * @param stick The joystick port number and the target axis
198 * @return What type of axis the axis is reporting to be
199 */
200int DriverStation::GetJoystickAxisType(uint32_t stick, uint8_t axis) const {
201 if (stick >= kJoystickPorts) {
202 wpi_setWPIError(BadJoystickIndex);
203 return -1;
204 }
205 return m_joystickDescriptor[stick].axisTypes[axis];
206}
207
208/**
209 * Returns the number of POVs on a given joystick port
210 *
211 * @param stick The joystick port number
212 * @return The number of POVs on the indicated joystick
213 */
214int DriverStation::GetStickPOVCount(uint32_t stick) const {
215 if (stick >= kJoystickPorts) {
216 wpi_setWPIError(BadJoystickIndex);
217 return 0;
218 }
219 HALJoystickPOVs joystickPOVs;
220 HALGetJoystickPOVs(stick, &joystickPOVs);
221 return joystickPOVs.count;
222}
223
224/**
225 * Returns the number of buttons on a given joystick port
226 *
227 * @param stick The joystick port number
228 * @return The number of buttons on the indicated joystick
229 */
230int DriverStation::GetStickButtonCount(uint32_t stick) const {
231 if (stick >= kJoystickPorts) {
232 wpi_setWPIError(BadJoystickIndex);
233 return 0;
234 }
235 HALJoystickButtons joystickButtons;
236 HALGetJoystickButtons(stick, &joystickButtons);
237 return joystickButtons.count;
238}
239
240/**
241 * Get the value of the axis on a joystick.
242 * This depends on the mapping of the joystick connected to the specified port.
243 *
244 * @param stick The joystick to read.
245 * @param axis The analog axis value to read from the joystick.
246 * @return The value of the axis on the joystick.
247 */
248float DriverStation::GetStickAxis(uint32_t stick, uint32_t axis) {
249 if (stick >= kJoystickPorts) {
250 wpi_setWPIError(BadJoystickIndex);
251 return 0;
252 }
253
254 if (axis >= m_joystickAxes[stick].count) {
255 if (axis >= kMaxJoystickAxes)
256 wpi_setWPIError(BadJoystickAxis);
257 else
258 ReportJoystickUnpluggedError(
259 "WARNING: Joystick Axis missing, check if all controllers are "
260 "plugged in\n");
261 return 0.0f;
262 }
263
264 int8_t value = m_joystickAxes[stick].axes[axis];
265
266 if (value < 0) {
267 return value / 128.0f;
268 } else {
269 return value / 127.0f;
270 }
271}
272
273/**
274 * Get the state of a POV on the joystick.
275 *
276 * @return the angle of the POV in degrees, or -1 if the POV is not pressed.
277 */
278int DriverStation::GetStickPOV(uint32_t stick, uint32_t pov) {
279 if (stick >= kJoystickPorts) {
280 wpi_setWPIError(BadJoystickIndex);
281 return -1;
282 }
283
284 if (pov >= m_joystickPOVs[stick].count) {
285 if (pov >= kMaxJoystickPOVs)
286 wpi_setWPIError(BadJoystickAxis);
287 else
288 ReportJoystickUnpluggedError(
289 "WARNING: Joystick POV missing, check if all controllers are plugged "
290 "in\n");
291 return -1;
292 }
293
294 return m_joystickPOVs[stick].povs[pov];
295}
296
297/**
298 * The state of the buttons on the joystick.
299 *
300 * @param stick The joystick to read.
301 * @return The state of the buttons on the joystick.
302 */
303uint32_t DriverStation::GetStickButtons(uint32_t stick) const {
304 if (stick >= kJoystickPorts) {
305 wpi_setWPIError(BadJoystickIndex);
306 return 0;
307 }
308
309 return m_joystickButtons[stick].buttons;
310}
311
312/**
313 * The state of one joystick button. Button indexes begin at 1.
314 *
315 * @param stick The joystick to read.
316 * @param button The button index, beginning at 1.
317 * @return The state of the joystick button.
318 */
319bool DriverStation::GetStickButton(uint32_t stick, uint8_t button) {
320 if (stick >= kJoystickPorts) {
321 wpi_setWPIError(BadJoystickIndex);
322 return false;
323 }
324
325 if (button > m_joystickButtons[stick].count) {
326 ReportJoystickUnpluggedError(
327 "WARNING: Joystick Button missing, check if all controllers are "
328 "plugged in\n");
329 return false;
330 }
331 if (button == 0) {
332 ReportJoystickUnpluggedError(
333 "ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
334 return false;
335 }
336 return ((0x1 << (button - 1)) & m_joystickButtons[stick].buttons) != 0;
337}
338
339/**
340 * Check if the DS has enabled the robot
341 * @return True if the robot is enabled and the DS is connected
342 */
343bool DriverStation::IsEnabled() const {
344 HALControlWord controlWord;
345 memset(&controlWord, 0, sizeof(controlWord));
346 HALGetControlWord(&controlWord);
347 return controlWord.enabled && controlWord.dsAttached;
348}
349
350/**
351 * Check if the robot is disabled
352 * @return True if the robot is explicitly disabled or the DS is not connected
353 */
354bool DriverStation::IsDisabled() const {
355 HALControlWord controlWord;
356 memset(&controlWord, 0, sizeof(controlWord));
357 HALGetControlWord(&controlWord);
358 return !(controlWord.enabled && controlWord.dsAttached);
359}
360
361/**
362 * Check if the DS is commanding autonomous mode
363 * @return True if the robot is being commanded to be in autonomous mode
364 */
365bool DriverStation::IsAutonomous() const {
366 HALControlWord controlWord;
367 memset(&controlWord, 0, sizeof(controlWord));
368 HALGetControlWord(&controlWord);
369 return controlWord.autonomous;
370}
371
372/**
373 * Check if the DS is commanding teleop mode
374 * @return True if the robot is being commanded to be in teleop mode
375 */
376bool DriverStation::IsOperatorControl() const {
377 HALControlWord controlWord;
378 memset(&controlWord, 0, sizeof(controlWord));
379 HALGetControlWord(&controlWord);
380 return !(controlWord.autonomous || controlWord.test);
381}
382
383/**
384 * Check if the DS is commanding test mode
385 * @return True if the robot is being commanded to be in test mode
386 */
387bool DriverStation::IsTest() const {
388 HALControlWord controlWord;
389 HALGetControlWord(&controlWord);
390 return controlWord.test;
391}
392
393/**
394 * Check if the DS is attached
395 * @return True if the DS is connected to the robot
396 */
397bool DriverStation::IsDSAttached() const {
398 HALControlWord controlWord;
399 memset(&controlWord, 0, sizeof(controlWord));
400 HALGetControlWord(&controlWord);
401 return controlWord.dsAttached;
402}
403
404/**
405 * Check if the FPGA outputs are enabled. The outputs may be disabled if the
406 * robot is disabled
407 * or e-stopped, the watchdog has expired, or if the roboRIO browns out.
408 * @return True if the FPGA outputs are enabled.
409 */
410bool DriverStation::IsSysActive() const {
411 int32_t status = 0;
412 bool retVal = HALGetSystemActive(&status);
413 wpi_setErrorWithContext(status, getHALErrorMessage(status));
414 return retVal;
415}
416
417/**
418 * Check if the system is browned out.
419 * @return True if the system is browned out
420 */
421bool DriverStation::IsSysBrownedOut() const {
422 int32_t status = 0;
423 bool retVal = HALGetBrownedOut(&status);
424 wpi_setErrorWithContext(status, getHALErrorMessage(status));
425 return retVal;
426}
427
428/**
429 * Has a new control packet from the driver station arrived since the last time
430 * this function was called?
431 * Warning: If you call this function from more than one place at the same time,
432 * you will not get the get the intended behaviour.
433 * @return True if the control data has been updated since the last call.
434 */
435bool DriverStation::IsNewControlData() const {
436 return m_newControlData.tryTake() == false;
437}
438
439/**
440 * Is the driver station attached to a Field Management System?
441 * @return True if the robot is competing on a field being controlled by a Field
442 * Management System
443 */
444bool DriverStation::IsFMSAttached() const {
445 HALControlWord controlWord;
446 HALGetControlWord(&controlWord);
447 return controlWord.fmsAttached;
448}
449
450/**
451 * Return the alliance that the driver station says it is on.
452 * This could return kRed or kBlue
453 * @return The Alliance enum (kRed, kBlue or kInvalid)
454 */
455DriverStation::Alliance DriverStation::GetAlliance() const {
456 HALAllianceStationID allianceStationID;
457 HALGetAllianceStation(&allianceStationID);
458 switch (allianceStationID) {
459 case kHALAllianceStationID_red1:
460 case kHALAllianceStationID_red2:
461 case kHALAllianceStationID_red3:
462 return kRed;
463 case kHALAllianceStationID_blue1:
464 case kHALAllianceStationID_blue2:
465 case kHALAllianceStationID_blue3:
466 return kBlue;
467 default:
468 return kInvalid;
469 }
470}
471
472/**
473 * Return the driver station location on the field
474 * This could return 1, 2, or 3
475 * @return The location of the driver station (1-3, 0 for invalid)
476 */
477uint32_t DriverStation::GetLocation() const {
478 HALAllianceStationID allianceStationID;
479 HALGetAllianceStation(&allianceStationID);
480 switch (allianceStationID) {
481 case kHALAllianceStationID_red1:
482 case kHALAllianceStationID_blue1:
483 return 1;
484 case kHALAllianceStationID_red2:
485 case kHALAllianceStationID_blue2:
486 return 2;
487 case kHALAllianceStationID_red3:
488 case kHALAllianceStationID_blue3:
489 return 3;
490 default:
491 return 0;
492 }
493}
494
495/**
496 * Wait until a new packet comes from the driver station
497 * This blocks on a semaphore, so the waiting is efficient.
498 * This is a good way to delay processing until there is new driver station data
499 * to act on
500 */
501void DriverStation::WaitForData() {
502 std::unique_lock<priority_mutex> lock(m_waitForDataMutex);
503 m_waitForDataCond.wait(lock);
504}
505
506/**
507 * Return the approximate match time
508 * The FMS does not send an official match time to the robots, but does send an
509 * approximate match time.
510 * The value will count down the time remaining in the current period (auto or
511 * teleop).
512 * Warning: This is not an official time (so it cannot be used to dispute ref
513 * calls or guarantee that a function
514 * will trigger before the match ends)
515 * The Practice Match function of the DS approximates the behaviour seen on the
516 * field.
517 * @return Time remaining in current match period (auto or teleop)
518 */
519double DriverStation::GetMatchTime() const {
520 float matchTime;
521 HALGetMatchTime(&matchTime);
522 return (double)matchTime;
523}
524
525/**
526 * Report an error to the DriverStation messages window.
527 * The error is also printed to the program console.
528 */
529void DriverStation::ReportError(std::string error) {
530 std::cout << error << std::endl;
531
532 HALControlWord controlWord;
533 HALGetControlWord(&controlWord);
534 if (controlWord.dsAttached) {
535 HALSetErrorData(error.c_str(), error.size(), 0);
536 }
537}