RobotCore 1.0
WPILib port to raspberry Pi
Loading...
Searching...
No Matches
DriverStationBackend.java
1// TODO - RobotCore - This file has been modified only to change the number of joystick ports.
2
3// Copyright (c) FIRST and other WPILib contributors.
4// Open Source Software; you can modify and/or share it under the terms of
5// the WPILib BSD license file in the root directory of this project.
6
7package org.wpilib.driverstation.internal;
8
9import java.util.HashMap;
10import java.util.Map;
11import java.util.Optional;
12import java.util.OptionalDouble;
13import java.util.OptionalInt;
14import java.util.concurrent.locks.ReentrantLock;
15import java.util.stream.Collectors;
16import org.wpilib.datalog.BooleanArrayLogEntry;
17import org.wpilib.datalog.DataLog;
18import org.wpilib.datalog.FloatArrayLogEntry;
19import org.wpilib.datalog.IntegerArrayLogEntry;
20import org.wpilib.datalog.StringLogEntry;
21import org.wpilib.datalog.StructLogEntry;
22import org.wpilib.driverstation.Alliance;
23import org.wpilib.driverstation.MatchType;
24import org.wpilib.driverstation.POVDirection;
25import org.wpilib.driverstation.TouchpadFinger;
26import org.wpilib.framework.OpModeRobot;
27import org.wpilib.framework.RobotBase;
28import org.wpilib.framework.TimedRobot;
29import org.wpilib.hardware.hal.AllianceStationID;
30import org.wpilib.hardware.hal.ControlWord;
31import org.wpilib.hardware.hal.DriverStationJNI;
32import org.wpilib.hardware.hal.HAL;
33import org.wpilib.hardware.hal.MatchInfoData;
34import org.wpilib.hardware.hal.OpModeOption;
35import org.wpilib.hardware.hal.RobotMode;
36import org.wpilib.networktables.BooleanPublisher;
37import org.wpilib.networktables.IntegerPublisher;
38import org.wpilib.networktables.NetworkTableInstance;
39import org.wpilib.networktables.StringPublisher;
40import org.wpilib.networktables.StringTopic;
41import org.wpilib.networktables.StructPublisher;
42import org.wpilib.util.Alert;
43import org.wpilib.util.AlertException;
44import org.wpilib.util.Color;
45import org.wpilib.util.UsageReporting;
46import org.wpilib.util.WPIUtilJNI;
47import org.wpilib.util.concurrent.EventVector;
48
50public final class DriverStationBackend {
52 public static final int JOYSTICK_PORTS = 1;
53
54 private static final long[] m_metadataCache = new long[7];
55 private static final float[] m_touchpadFingersCache = new float[8];
56
57 private static int availableToCount(long available) {
58 // Top bit has to be set
59 if (available < 0) {
60 return 64;
61 }
62
63 int count = 0;
64
65 // Top bit not set, we will eventually get a 0 bit
66 while ((available & 0x1) != 0) {
67 count++;
68 available >>= 1;
69 }
70 return count;
71 }
72
73 private static final class HALJoystickTouchpadFinger {
74 float m_x;
75 float m_y;
76 boolean m_down;
77 }
78
79 private static class HALJoystickTouchpad {
80 final HALJoystickTouchpadFinger[] m_fingers =
81 new HALJoystickTouchpadFinger[DriverStationJNI.MAX_JOYSTICK_TOUCHPAD_FINGERS];
82 int m_count;
83
84 HALJoystickTouchpad() {
85 for (int i = 0; i < m_fingers.length; i++) {
86 m_fingers[i] = new HALJoystickTouchpadFinger();
87 }
88 }
89 }
90
91 private static class HALJoystickTouchpads {
92 final HALJoystickTouchpad[] m_touchpads =
93 new HALJoystickTouchpad[DriverStationJNI.MAX_JOYSTICK_TOUCHPADS];
94 int m_count;
95
96 HALJoystickTouchpads() {
97 for (int i = 0; i < m_touchpads.length; i++) {
98 m_touchpads[i] = new HALJoystickTouchpad();
99 }
100 }
101 }
102
103 private static final class HALJoystickButtons {
104 long m_buttons;
105 long m_available;
106 }
107
108 private static class HALJoystickAxes {
109 final float[] m_axes;
110 int m_available;
111
112 HALJoystickAxes(int count) {
113 m_axes = new float[count];
114 }
115 }
116
117 private static class HALJoystickAxesRaw {
118 final short[] m_axes;
119
120 @SuppressWarnings("unused")
121 int m_available;
122
123 HALJoystickAxesRaw(int count) {
124 m_axes = new short[count];
125 }
126 }
127
128 private static class HALJoystickPOVs {
129 final byte[] m_povs;
130 int m_available;
131
132 HALJoystickPOVs(int count) {
133 m_povs = new byte[count];
134 for (int i = 0; i < count; i++) {
135 m_povs[i] = 0;
136 }
137 }
138 }
139
140 private static String opModeToString(long id) {
141 if (id == 0) {
142 return "";
143 }
144 m_opModesMutex.lock();
145 try {
146 OpModeOption option = m_opModes.get(id);
147 if (option != null) {
148 return option.name;
149 }
150 } finally {
151 m_opModesMutex.unlock();
152 }
153 return "<" + id + ">";
154 }
155
156 private static class MatchDataSender {
157 private static final String SMART_DASHBOARD_TYPE = "DriverStation";
158
159 final StringPublisher gameData;
160 final StringPublisher eventName;
161 final IntegerPublisher matchNumber;
162 final IntegerPublisher replayNumber;
163 final IntegerPublisher matchType;
164 final BooleanPublisher alliance;
165 final IntegerPublisher station;
166 final IntegerPublisher allianceStation;
167 final StructPublisher<ControlWord> controlWord;
168 final StringPublisher opMode;
169 String oldEventName = "";
170 String oldGameData = "";
171 int oldMatchNumber;
172 int oldReplayNumber;
173 int oldMatchType;
174 boolean oldIsRedAlliance = true;
175 int oldStationNumber = 1;
176 int oldAllianceStation = 0;
177 final ControlWord oldControlWord = new ControlWord();
178 final ControlWord currentControlWord = new ControlWord();
179
180 MatchDataSender() {
181 var table = NetworkTableInstance.getDefault().getTable("DriverStation");
182 table
183 .getStringTopic(".type")
184 .publishEx(
185 StringTopic.TYPE_STRING, "{\"SmartDashboard\":\"" + SMART_DASHBOARD_TYPE + "\"}")
186 .set(SMART_DASHBOARD_TYPE);
187 gameData = table.getStringTopic("GameData").publish();
188 gameData.set("");
189 eventName = table.getStringTopic("EventName").publish();
190 eventName.set("");
191 matchNumber = table.getIntegerTopic("MatchNumber").publish();
192 matchNumber.set(0);
193 replayNumber = table.getIntegerTopic("ReplayNumber").publish();
194 replayNumber.set(0);
195 matchType = table.getIntegerTopic("MatchType").publish();
196 matchType.set(0);
197 alliance = table.getBooleanTopic("IsRedAlliance").publish();
198 alliance.set(true);
199 station = table.getIntegerTopic("StationNumber").publish();
200 station.set(1);
201 allianceStation = table.getIntegerTopic("AllianceStationID").publish();
202 allianceStation.set(0);
203 controlWord = table.getStructTopic("ControlWord", ControlWord.struct).publish();
204 controlWord.set(oldControlWord);
205 opMode = table.getStringTopic("OpMode").publish();
206 opMode.set("");
207 }
208
209 @SuppressWarnings("VariableDeclarationUsageDistance")
210 private void sendMatchData() {
211 AllianceStationID allianceID = DriverStationJNI.getAllianceStation();
212 final int stationNumber =
213 switch (allianceID) {
214 case BLUE_1, RED_1 -> 1;
215 case BLUE_2, RED_2 -> 2;
216 case BLUE_3, RED_3 -> 3;
217 case UNKNOWN -> 0;
218 };
219 final boolean isRedAlliance =
220 switch (allianceID) {
221 case BLUE_1, BLUE_2, BLUE_3 -> false;
222 case RED_1, RED_2, RED_3, UNKNOWN -> true;
223 };
224
225 String currentEventName;
226 String currentGameData;
227 int currentMatchNumber;
228 int currentReplayNumber;
229 int currentMatchType;
230 m_cacheDataMutex.lock();
231 try {
232 currentEventName = DriverStationBackend.m_matchInfo.eventName;
233 currentGameData = DriverStationBackend.m_gameData;
234 currentMatchNumber = DriverStationBackend.m_matchInfo.matchNumber;
235 currentReplayNumber = DriverStationBackend.m_matchInfo.replayNumber;
236 currentMatchType = DriverStationBackend.m_matchInfo.matchType;
237 } finally {
238 m_cacheDataMutex.unlock();
239 }
240 DriverStationJNI.getControlWord(currentControlWord);
241
242 if (oldIsRedAlliance != isRedAlliance) {
243 alliance.set(isRedAlliance);
244 oldIsRedAlliance = isRedAlliance;
245 }
246 if (oldStationNumber != stationNumber) {
247 station.set(stationNumber);
248 oldStationNumber = stationNumber;
249 }
250 if (oldAllianceStation != allianceID.ordinal()) {
251 allianceStation.set(allianceID.ordinal());
252 oldAllianceStation = allianceID.ordinal();
253 }
254 if (!oldEventName.equals(currentEventName)) {
255 eventName.set(currentEventName);
256 oldEventName = currentEventName;
257 }
258 if (!oldGameData.equals(currentGameData)) {
259 gameData.set(currentGameData);
260 oldGameData = currentGameData;
261 }
262 if (currentMatchNumber != oldMatchNumber) {
263 matchNumber.set(currentMatchNumber);
264 oldMatchNumber = currentMatchNumber;
265 }
266 if (currentReplayNumber != oldReplayNumber) {
267 replayNumber.set(currentReplayNumber);
268 oldReplayNumber = currentReplayNumber;
269 }
270 if (currentMatchType != oldMatchType) {
271 matchType.set(currentMatchType);
272 oldMatchType = currentMatchType;
273 }
274 if (!currentControlWord.equals(oldControlWord)) {
275 long currentOpModeId = currentControlWord.getOpModeId();
276 if (currentOpModeId != oldControlWord.getOpModeId()) {
277 opMode.set(opModeToString(currentOpModeId));
278 }
279 controlWord.set(currentControlWord);
280 oldControlWord.update(currentControlWord);
281 }
282 }
283 }
284
285 private static class JoystickLogSender {
286 JoystickLogSender(DataLog log, int stick, long timestamp) {
287 m_stick = stick;
288
289 m_logButtons = new BooleanArrayLogEntry(log, "DS:joystick" + stick + "/buttons", timestamp);
290 m_logAxes = new FloatArrayLogEntry(log, "DS:joystick" + stick + "/axes", timestamp);
291 m_logPOVs = new IntegerArrayLogEntry(log, "DS:joystick" + stick + "/povs", timestamp);
292
293 appendButtons(m_joystickButtons[m_stick], timestamp);
294 appendAxes(m_joystickAxes[m_stick], timestamp);
295 appendPOVs(m_joystickPOVs[m_stick], timestamp);
296 }
297
298 public void send(long timestamp) {
299 HALJoystickButtons buttons = m_joystickButtons[m_stick];
300 if (buttons.m_available != m_prevButtons.m_available
301 || buttons.m_buttons != m_prevButtons.m_buttons) {
302 appendButtons(buttons, timestamp);
303 }
304
305 HALJoystickAxes axes = m_joystickAxes[m_stick];
306 int available = axes.m_available;
307 boolean needToLog = false;
308 if (available != m_prevAxes.m_available) {
309 needToLog = true;
310 } else {
311 for (int i = 0; i < m_prevAxes.m_axes.length; i++) {
312 if (axes.m_axes[i] != m_prevAxes.m_axes[i]) {
313 needToLog = true;
314 break;
315 }
316 }
317 }
318 if (needToLog) {
319 appendAxes(axes, timestamp);
320 }
321
322 HALJoystickPOVs povs = m_joystickPOVs[m_stick];
323 available = m_joystickPOVs[m_stick].m_available;
324 needToLog = false;
325 if (available != m_prevPOVs.m_available) {
326 needToLog = true;
327 } else {
328 for (int i = 0; i < m_prevPOVs.m_povs.length; i++) {
329 if (povs.m_povs[i] != m_prevPOVs.m_povs[i]) {
330 needToLog = true;
331 break;
332 }
333 }
334 }
335 if (needToLog) {
336 appendPOVs(povs, timestamp);
337 }
338 }
339
340 void appendButtons(HALJoystickButtons buttons, long timestamp) {
341 int count = availableToCount(buttons.m_available);
342 if (m_sizedButtons == null || m_sizedButtons.length != count) {
343 m_sizedButtons = new boolean[count];
344 }
345 long buttonsValue = buttons.m_buttons;
346 for (int i = 0; i < count; i++) {
347 m_sizedButtons[i] = (buttonsValue & (1L << i)) != 0;
348 }
349 m_logButtons.append(m_sizedButtons, timestamp);
350 m_prevButtons.m_available = buttons.m_available;
351 m_prevButtons.m_buttons = buttons.m_buttons;
352 }
353
354 void appendAxes(HALJoystickAxes axes, long timestamp) {
355 int count = availableToCount(axes.m_available);
356 if (m_sizedAxes == null || m_sizedAxes.length != count) {
357 m_sizedAxes = new float[count];
358 }
359 System.arraycopy(axes.m_axes, 0, m_sizedAxes, 0, count);
360 m_logAxes.append(m_sizedAxes, timestamp);
361 m_prevAxes.m_available = axes.m_available;
362 System.arraycopy(axes.m_axes, 0, m_prevAxes.m_axes, 0, count);
363 }
364
365 void appendPOVs(HALJoystickPOVs povs, long timestamp) {
366 int count = availableToCount(povs.m_available);
367 if (m_sizedPOVs == null || m_sizedPOVs.length != count) {
368 m_sizedPOVs = new long[count];
369 }
370 for (int i = 0; i < count; i++) {
371 m_sizedPOVs[i] = povs.m_povs[i];
372 }
373 m_logPOVs.append(m_sizedPOVs, timestamp);
374 m_prevPOVs.m_available = povs.m_available;
375 System.arraycopy(povs.m_povs, 0, m_prevPOVs.m_povs, 0, count);
376 }
377
378 final int m_stick;
379 boolean[] m_sizedButtons;
380 float[] m_sizedAxes;
381 long[] m_sizedPOVs;
382 final HALJoystickButtons m_prevButtons = new HALJoystickButtons();
383 final HALJoystickAxes m_prevAxes = new HALJoystickAxes(DriverStationJNI.MAX_JOYSTICK_AXES);
384 final HALJoystickPOVs m_prevPOVs = new HALJoystickPOVs(DriverStationJNI.MAX_JOYSTICK_POVS);
385 final BooleanArrayLogEntry m_logButtons;
386 final FloatArrayLogEntry m_logAxes;
387 final IntegerArrayLogEntry m_logPOVs;
388 }
389
390 private static class DataLogSender {
391 DataLogSender(DataLog log, boolean logJoysticks, long timestamp) {
392 m_logControlWord =
393 StructLogEntry.create(log, "DS:controlWord", ControlWord.struct, timestamp);
394
395 // append initial control word value
396 m_logControlWord.append(m_controlWordCache, timestamp);
397 m_oldControlWord.update(m_controlWordCache);
398
399 // append initial opmode value
400 m_logOpMode = new StringLogEntry(log, "DS:opMode", timestamp);
401 m_logOpMode.append(m_opModeCache, timestamp);
402
403 if (logJoysticks) {
404 m_joysticks = new JoystickLogSender[JOYSTICK_PORTS];
405 for (int i = 0; i < JOYSTICK_PORTS; i++) {
406 m_joysticks[i] = new JoystickLogSender(log, i, timestamp);
407 }
408 } else {
409 m_joysticks = new JoystickLogSender[0];
410 }
411 }
412
413 public void send(long timestamp) {
414 // append control word value changes
415 if (!m_controlWordCache.equals(m_oldControlWord)) {
416 // append opmode value changes
417 long opModeId = m_controlWordCache.getOpModeId();
418 if (opModeId != m_oldControlWord.getOpModeId()) {
419 m_logOpMode.append(m_opModeCache, timestamp);
420 }
421
422 m_logControlWord.append(m_controlWordCache, timestamp);
423 m_oldControlWord.update(m_controlWordCache);
424 }
425
426 // append joystick value changes
427 for (JoystickLogSender joystick : m_joysticks) {
428 joystick.send(timestamp);
429 }
430 }
431
432 final ControlWord m_oldControlWord = new ControlWord();
433 final StructLogEntry<ControlWord> m_logControlWord;
434 final StringLogEntry m_logOpMode;
435
436 final JoystickLogSender[] m_joysticks;
437 }
438
439 // Joystick User Data
440 private static HALJoystickAxes[] m_joystickAxes = new HALJoystickAxes[JOYSTICK_PORTS];
441 private static HALJoystickAxesRaw[] m_joystickAxesRaw = new HALJoystickAxesRaw[JOYSTICK_PORTS];
442 private static HALJoystickPOVs[] m_joystickPOVs = new HALJoystickPOVs[JOYSTICK_PORTS];
443 private static HALJoystickButtons[] m_joystickButtons = new HALJoystickButtons[JOYSTICK_PORTS];
444 private static HALJoystickTouchpads[] m_joystickTouchpads =
445 new HALJoystickTouchpads[JOYSTICK_PORTS];
446 private static MatchInfoData m_matchInfo = new MatchInfoData();
447 private static String m_gameData = "";
448 private static ControlWord m_controlWord = new ControlWord();
449 private static String m_opMode = "";
450 private static EventVector m_refreshEvents = new EventVector();
451
452 // Joystick Cached Data
453 private static HALJoystickAxes[] m_joystickAxesCache = new HALJoystickAxes[JOYSTICK_PORTS];
454 private static HALJoystickAxesRaw[] m_joystickAxesRawCache =
455 new HALJoystickAxesRaw[JOYSTICK_PORTS];
456 private static HALJoystickPOVs[] m_joystickPOVsCache = new HALJoystickPOVs[JOYSTICK_PORTS];
457 private static HALJoystickButtons[] m_joystickButtonsCache =
458 new HALJoystickButtons[JOYSTICK_PORTS];
459 private static HALJoystickTouchpads[] m_joystickTouchpadsCache =
460 new HALJoystickTouchpads[JOYSTICK_PORTS];
461 private static MatchInfoData m_matchInfoCache = new MatchInfoData();
462 private static String m_gameDataCache = "";
463 private static ControlWord m_controlWordCache = new ControlWord();
464
465 private static String m_opModeCache = "";
466
467 // Joystick button rising/falling edge flags
468 private static long[] m_joystickButtonsPressed = new long[JOYSTICK_PORTS];
469 private static long[] m_joystickButtonsReleased = new long[JOYSTICK_PORTS];
470
471 private static final MatchDataSender m_matchDataSender;
472 private static DataLogSender m_dataLogSender;
473
474 private static final ReentrantLock m_cacheDataMutex = new ReentrantLock();
475
476 private enum JoystickResourceAlert {
477 BUTTON("ButtonUnavailable", "Button"),
478 AXIS("AxisUnavailable", "axis"),
479 POV("POVUnavailable", "POV");
480
481 final String idSuffix;
482 final String label;
483
484 JoystickResourceAlert(String idSuffix, String label) {
485 this.idSuffix = idSuffix;
486 this.label = label;
487 }
488
489 boolean isAvailable(int stick, int resource) {
490 return switch (this) {
491 case BUTTON -> (getStickButtonsAvailable(stick) & (1L << resource)) != 0;
492 case AXIS -> (getStickAxesAvailable(stick) & (1 << resource)) != 0;
493 case POV -> (getStickPOVsAvailable(stick) & (1 << resource)) != 0;
494 };
495 }
496
497 String getText(int stick, int resource) {
498 return "Joystick " + label + " " + resource + " on port " + stick + " not available";
499 }
500 }
501
502 private static final JoystickResourceAlert[] JOYSTICK_RESOURCE_ALERTS =
503 JoystickResourceAlert.values();
504
505 private static final class JoystickAlertState {
506 Alert alert;
507 boolean active;
508 }
509
510 private static final class JoystickAlerts {
511 final int stick;
512 final String connectionText;
513 final JoystickAlertState connectionAlert = new JoystickAlertState();
514 final JoystickAlertState[] resourceAlerts =
515 new JoystickAlertState[JOYSTICK_RESOURCE_ALERTS.length];
516 final JoystickAlertState touchpadFingerAlert = new JoystickAlertState();
517 final int[] resourceAlertValues = new int[JOYSTICK_RESOURCE_ALERTS.length];
518 int touchpadFingerAlertTouchpad;
519 int touchpadFingerAlertFinger;
520 boolean initialized;
521
522 JoystickAlerts(int stick) {
523 this.stick = stick;
524 connectionText =
525 "Joystick on port " + stick + " not available, check if controller is plugged in";
526 for (int i = 0; i < resourceAlerts.length; i++) {
527 resourceAlerts[i] = new JoystickAlertState();
528 }
529 initializeAlerts();
530 }
531
532 boolean initializeAlerts() {
533 closeAlerts();
534
535 Alert newConnectionAlert = null;
536 Alert[] newResourceAlerts = new Alert[JOYSTICK_RESOURCE_ALERTS.length];
537 Alert newTouchpadFingerAlert = null;
538
539 try {
540 newConnectionAlert =
541 new Alert(
542 "DriverStation",
543 "joystick" + stick + "Disconnected",
544 connectionText,
545 Alert.Level.HIGH);
546 for (JoystickResourceAlert resourceAlert : JOYSTICK_RESOURCE_ALERTS) {
547 newResourceAlerts[resourceAlert.ordinal()] =
548 new Alert(
549 "DriverStation",
550 "joystick" + stick + resourceAlert.idSuffix,
551 "",
552 Alert.Level.MEDIUM);
553 }
554 newTouchpadFingerAlert =
555 new Alert(
556 "DriverStation",
557 "joystick" + stick + "TouchpadFingerUnavailable",
558 "",
559 Alert.Level.MEDIUM);
560 } catch (AlertException ex) {
561 closeAlert(newConnectionAlert);
562 for (Alert alert : newResourceAlerts) {
563 closeAlert(alert);
564 }
565 closeAlert(newTouchpadFingerAlert);
566 initialized = false;
567 return false;
568 }
569
570 connectionAlert.alert = newConnectionAlert;
571 for (JoystickResourceAlert resourceAlert : JOYSTICK_RESOURCE_ALERTS) {
572 resourceAlerts[resourceAlert.ordinal()].alert = newResourceAlerts[resourceAlert.ordinal()];
573 }
574 touchpadFingerAlert.alert = newTouchpadFingerAlert;
575 initialized = true;
576 return true;
577 }
578
579 void closeAlerts() {
580 closeAlert(connectionAlert);
581 for (JoystickAlertState alertState : resourceAlerts) {
582 closeAlert(alertState);
583 }
584 closeAlert(touchpadFingerAlert);
585 initialized = false;
586 }
587
588 static void closeAlert(JoystickAlertState alertState) {
589 closeAlert(alertState.alert);
590 alertState.alert = null;
591 alertState.active = false;
592 }
593
594 static void closeAlert(Alert alert) {
595 if (alert == null) {
596 return;
597 }
598
599 try {
600 alert.close();
601 } catch (AlertException ex) {
602 // Ignore stale handles while cleaning up a failed initialization attempt.
603 }
604 }
605
606 boolean prepareForReport() {
607 if (isJoystickConnected(stick)) {
608 setAlert(connectionAlert, false, connectionText, true);
609 return true;
610 }
611
612 clearResourceAlerts();
613 setAlert(connectionAlert, true, connectionText, true);
614 return false;
615 }
616
617 void refresh() {
618 if (!isJoystickConnected(stick)) {
619 clearResourceAlerts();
620 return;
621 }
622
623 if (connectionAlert.active) {
624 setAlert(connectionAlert, false, connectionText, true);
625 }
626
627 for (JoystickResourceAlert resourceAlert : JOYSTICK_RESOURCE_ALERTS) {
628 refreshResourceAlert(resourceAlert);
629 }
630
631 if (touchpadFingerAlert.active
632 && getStickTouchpadFingerAvailable(
633 stick, touchpadFingerAlertTouchpad, touchpadFingerAlertFinger)) {
634 setAlert(touchpadFingerAlert, false);
635 }
636 }
637
638 void clearAllAlerts() {
639 setAlert(connectionAlert, false, connectionText, true);
640 clearResourceAlerts();
641 }
642
643 void clearResourceAlerts() {
644 for (JoystickAlertState alertState : resourceAlerts) {
645 setAlert(alertState, false);
646 }
647 setAlert(touchpadFingerAlert, false);
648 }
649
650 void refreshResourceAlert(JoystickResourceAlert resourceAlert) {
651 int alertIndex = resourceAlert.ordinal();
652 JoystickAlertState alertState = resourceAlerts[alertIndex];
653 if (alertState.active && resourceAlert.isAvailable(stick, resourceAlertValues[alertIndex])) {
654 setAlert(alertState, false);
655 }
656 }
657
658 void reportResourceWarning(JoystickResourceAlert resourceAlert, int resource) {
659 int alertIndex = resourceAlert.ordinal();
660 resourceAlertValues[alertIndex] = resource;
661 setAlert(resourceAlerts[alertIndex], true, resourceAlert.getText(stick, resource), false);
662 }
663
664 void reportTouchpadFingerWarning(int touchpad, int finger) {
665 String text =
666 "Joystick touchpad finger "
667 + finger
668 + " on touchpad "
669 + touchpad
670 + " on port "
671 + stick
672 + " not available";
673 touchpadFingerAlertTouchpad = touchpad;
674 touchpadFingerAlertFinger = finger;
675 setAlert(touchpadFingerAlert, true, text, false);
676 }
677
678 boolean applyAlert(
679 JoystickAlertState alertState, boolean active, String text, boolean verifyActive) {
680 if (alertState.alert == null) {
681 return false;
682 }
683
684 try {
685 if (active && !text.isEmpty()) {
686 alertState.alert.setText(text);
687 }
688 alertState.alert.set(active);
689 return !active || !verifyActive || alertState.alert.get();
690 } catch (AlertException ex) {
691 return false;
692 }
693 }
694
695 boolean setAlert(JoystickAlertState alertState, boolean active) {
696 return setAlert(alertState, active, "", false);
697 }
698
699 boolean setAlert(
700 JoystickAlertState alertState, boolean active, String text, boolean verifyActive) {
701 if (!active && !alertState.active) {
702 return true;
703 }
704
705 if (active && !initialized && !initializeAlerts()) {
706 return false;
707 }
708
709 if (!applyAlert(alertState, active, text, verifyActive)) {
710 if (!active) {
711 alertState.active = false;
712 return true;
713 }
714 if (!initializeAlerts()) {
715 return false;
716 }
717 if (!applyAlert(alertState, active, text, verifyActive)) {
718 return false;
719 }
720 }
721 alertState.active = active;
722 return true;
723 }
724 }
725
726 private static final ReentrantLock m_joystickAlertMutex = new ReentrantLock();
727 private static boolean m_silenceJoystickAlerts;
728 private static final JoystickAlerts[] m_joystickAlerts = new JoystickAlerts[JOYSTICK_PORTS];
729
730 private static volatile boolean m_userProgramStarted = false;
731 private static final Map<Long, OpModeOption> m_opModes = new HashMap<>();
732 private static final ReentrantLock m_opModesMutex = new ReentrantLock();
733
740 private DriverStationBackend() {}
741
742 static {
743 HAL.initialize();
744
745 for (int i = 0; i < JOYSTICK_PORTS; i++) {
746 m_joystickButtons[i] = new HALJoystickButtons();
747 m_joystickAxes[i] = new HALJoystickAxes(DriverStationJNI.MAX_JOYSTICK_AXES);
748 m_joystickAxesRaw[i] = new HALJoystickAxesRaw(DriverStationJNI.MAX_JOYSTICK_AXES);
749 m_joystickPOVs[i] = new HALJoystickPOVs(DriverStationJNI.MAX_JOYSTICK_POVS);
750 m_joystickTouchpads[i] = new HALJoystickTouchpads();
751
752 m_joystickButtonsCache[i] = new HALJoystickButtons();
753 m_joystickAxesCache[i] = new HALJoystickAxes(DriverStationJNI.MAX_JOYSTICK_AXES);
754 m_joystickAxesRawCache[i] = new HALJoystickAxesRaw(DriverStationJNI.MAX_JOYSTICK_AXES);
755 m_joystickPOVsCache[i] = new HALJoystickPOVs(DriverStationJNI.MAX_JOYSTICK_POVS);
756 m_joystickTouchpadsCache[i] = new HALJoystickTouchpads();
757 }
758
759 m_matchDataSender = new MatchDataSender();
760 }
761
768 public static void reportError(String error, boolean printTrace) {
769 reportErrorImpl(true, 1, error, printTrace);
770 }
771
778 public static void reportError(String error, StackTraceElement[] stackTrace) {
779 reportErrorImpl(true, 1, error, stackTrace);
780 }
781
788 public static void reportWarning(String warning, boolean printTrace) {
789 reportErrorImpl(false, 1, warning, printTrace);
790 }
791
798 public static void reportWarning(String warning, StackTraceElement[] stackTrace) {
799 reportErrorImpl(false, 1, warning, stackTrace);
800 }
801
802 private static void reportErrorImpl(boolean isError, int code, String error, boolean printTrace) {
803 reportErrorImpl(isError, code, error, printTrace, Thread.currentThread().getStackTrace(), 3);
804 }
805
806 private static void reportErrorImpl(
807 boolean isError, int code, String error, StackTraceElement[] stackTrace) {
808 reportErrorImpl(isError, code, error, true, stackTrace, 0);
809 }
810
811 private static void reportErrorImpl(
812 boolean isError,
813 int code,
814 String error,
815 boolean printTrace,
816 StackTraceElement[] stackTrace,
817 int stackTraceFirst) {
818 String locString;
819 if (stackTrace.length >= stackTraceFirst + 1) {
820 locString = stackTrace[stackTraceFirst].toString();
821 } else {
822 locString = "";
823 }
824 StringBuilder traceString = new StringBuilder();
825 if (printTrace) {
826 boolean haveLoc = false;
827 for (int i = stackTraceFirst; i < stackTrace.length; i++) {
828 String loc = stackTrace[i].toString();
829 traceString.append("\tat ").append(loc).append('\n');
830 // get first user function
831 if (!haveLoc && !loc.startsWith("org.wpilib")) {
832 locString = loc;
833 haveLoc = true;
834 }
835 }
836 }
837 DriverStationJNI.sendError(isError, code, error, locString, traceString.toString(), true);
838 }
839
846 public static void reportCrash(String error, StackTraceElement[] stackTrace) {
847 reportCrashImpl(error, stackTrace, 0);
848 }
849
850 private static void reportCrashImpl(
851 String error, StackTraceElement[] stackTrace, int stackTraceFirst) {
852 String locString;
853 if (stackTrace.length >= stackTraceFirst + 1) {
854 locString = stackTrace[stackTraceFirst].toString();
855 } else {
856 locString = "";
857 }
858 StringBuilder traceString = new StringBuilder();
859 boolean haveLoc = false;
860 for (int i = stackTraceFirst; i < stackTrace.length; i++) {
861 String loc = stackTrace[i].toString();
862 traceString.append("\tat ").append(loc).append('\n');
863 // get first user function
864 if (!haveLoc && !loc.startsWith("org.wpilib")) {
865 locString = loc;
866 haveLoc = true;
867 }
868 }
869 DriverStationJNI.sendProgramCrash(error, locString, traceString.toString());
870 // Sleep to ensure message is sent before crash
871 try {
872 Thread.sleep(100);
873 } catch (InterruptedException e) {
874 Thread.currentThread().interrupt();
875 }
876 }
877
885 public static boolean getStickButton(final int stick, final int button) {
886 if (stick < 0 || stick >= JOYSTICK_PORTS) {
887 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
888 }
889 if (button < 0 || button >= DriverStationJNI.MAX_JOYSTICK_BUTTONS) {
890 throw new IllegalArgumentException("Joystick Button is out of range");
891 }
892
893 long mask = 1L << button;
894
895 m_cacheDataMutex.lock();
896 try {
897 if ((m_joystickButtons[stick].m_available & mask) != 0) {
898 return (m_joystickButtons[stick].m_buttons & mask) != 0;
899 }
900 } finally {
901 m_cacheDataMutex.unlock();
902 }
903
904 reportJoystickResourceWarning(stick, JoystickResourceAlert.BUTTON, button);
905 return false;
906 }
907
915 public static Optional<Boolean> getStickButtonIfAvailable(final int stick, final int button) {
916 if (stick < 0 || stick >= JOYSTICK_PORTS) {
917 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
918 }
919 if (button < 0 || button >= DriverStationJNI.MAX_JOYSTICK_BUTTONS) {
920 throw new IllegalArgumentException("Joystick Button is out of range");
921 }
922
923 long mask = 1L << button;
924
925 m_cacheDataMutex.lock();
926 try {
927 if ((m_joystickButtons[stick].m_available & mask) != 0) {
928 return Optional.of((m_joystickButtons[stick].m_buttons & mask) != 0);
929 }
930 } finally {
931 m_cacheDataMutex.unlock();
932 }
933 return Optional.empty();
934 }
935
943 public static boolean getStickButtonPressed(final int stick, final int button) {
944 if (stick < 0 || stick >= JOYSTICK_PORTS) {
945 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
946 }
947 if (button < 0 || button >= DriverStationJNI.MAX_JOYSTICK_BUTTONS) {
948 throw new IllegalArgumentException("Joystick Button is out of range");
949 }
950
951 long mask = 1L << button;
952
953 m_cacheDataMutex.lock();
954 try {
955 if ((m_joystickButtons[stick].m_available & mask) != 0) {
956 // If button was pressed, clear flag and return true
957 if ((m_joystickButtonsPressed[stick] & mask) != 0) {
958 m_joystickButtonsPressed[stick] &= ~mask;
959 return true;
960 } else {
961 return false;
962 }
963 }
964 } finally {
965 m_cacheDataMutex.unlock();
966 }
967
968 reportJoystickResourceWarning(stick, JoystickResourceAlert.BUTTON, button);
969 return false;
970 }
971
979 public static boolean getStickButtonReleased(final int stick, final int button) {
980 if (stick < 0 || stick >= JOYSTICK_PORTS) {
981 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
982 }
983 if (button < 0 || button >= DriverStationJNI.MAX_JOYSTICK_BUTTONS) {
984 throw new IllegalArgumentException("Joystick Button is out of range");
985 }
986
987 long mask = 1L << button;
988
989 m_cacheDataMutex.lock();
990 try {
991 if ((m_joystickButtons[stick].m_available & mask) != 0) {
992 // If button was released, clear flag and return true
993 if ((m_joystickButtonsReleased[stick] & mask) != 0) {
994 m_joystickButtonsReleased[stick] &= ~mask;
995 return true;
996 } else {
997 return false;
998 }
999 }
1000 } finally {
1001 m_cacheDataMutex.unlock();
1002 }
1003
1004 reportJoystickResourceWarning(stick, JoystickResourceAlert.BUTTON, button);
1005 return false;
1006 }
1007
1016 public static double getStickAxis(int stick, int axis) {
1017 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1018 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1019 }
1020 if (axis < 0 || axis >= DriverStationJNI.MAX_JOYSTICK_AXES) {
1021 throw new IllegalArgumentException("Joystick axis is out of range");
1022 }
1023
1024 int mask = 1 << axis;
1025
1026 m_cacheDataMutex.lock();
1027 try {
1028 if ((m_joystickAxes[stick].m_available & mask) != 0) {
1029 return m_joystickAxes[stick].m_axes[axis];
1030 }
1031 } finally {
1032 m_cacheDataMutex.unlock();
1033 }
1034
1035 reportJoystickResourceWarning(stick, JoystickResourceAlert.AXIS, axis);
1036 return 0.0;
1037 }
1038
1047 public static TouchpadFinger getStickTouchpadFinger(int stick, int touchpad, int finger) {
1048 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1049 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1050 }
1051 if (touchpad < 0 || touchpad >= DriverStationJNI.MAX_JOYSTICK_TOUCHPADS) {
1052 throw new IllegalArgumentException("Joystick touchpad is out of range");
1053 }
1054 if (finger < 0 || finger >= DriverStationJNI.MAX_JOYSTICK_TOUCHPAD_FINGERS) {
1055 throw new IllegalArgumentException("Joystick touchpad finger is out of range");
1056 }
1057
1058 int touchpadCount;
1059 m_cacheDataMutex.lock();
1060 try {
1061 touchpadCount = m_joystickTouchpads[stick].m_count;
1062 if (touchpad < touchpadCount) {
1063 HALJoystickTouchpad tp = m_joystickTouchpads[stick].m_touchpads[touchpad];
1064 if (finger < tp.m_count) {
1065 return new TouchpadFinger(
1066 tp.m_fingers[finger].m_down, tp.m_fingers[finger].m_x, tp.m_fingers[finger].m_y);
1067 }
1068 }
1069 } finally {
1070 m_cacheDataMutex.unlock();
1071 }
1072
1073 reportJoystickTouchpadFingerWarning(stick, touchpad, finger);
1074 return new TouchpadFinger(false, 0.0f, 0.0f);
1075 }
1076
1085 public static boolean getStickTouchpadFingerAvailable(int stick, int touchpad, int finger) {
1086 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1087 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1088 }
1089 if (touchpad < 0 || touchpad >= DriverStationJNI.MAX_JOYSTICK_TOUCHPADS) {
1090 throw new IllegalArgumentException("Joystick touchpad is out of range");
1091 }
1092 if (finger < 0 || finger >= DriverStationJNI.MAX_JOYSTICK_TOUCHPAD_FINGERS) {
1093 throw new IllegalArgumentException("Joystick touchpad finger is out of range");
1094 }
1095
1096 int touchpadCount;
1097 m_cacheDataMutex.lock();
1098 try {
1099 touchpadCount = m_joystickTouchpads[stick].m_count;
1100 if (touchpad < touchpadCount) {
1101 HALJoystickTouchpad tp = m_joystickTouchpads[stick].m_touchpads[touchpad];
1102 if (finger < tp.m_count) {
1103 return true;
1104 }
1105 }
1106 } finally {
1107 m_cacheDataMutex.unlock();
1108 }
1109
1110 return false;
1111 }
1112
1121 public static OptionalDouble getStickAxisIfAvailable(int stick, int axis) {
1122 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1123 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1124 }
1125 if (axis < 0 || axis >= DriverStationJNI.MAX_JOYSTICK_AXES) {
1126 throw new IllegalArgumentException("Joystick axis is out of range");
1127 }
1128
1129 int mask = 1 << axis;
1130
1131 m_cacheDataMutex.lock();
1132 try {
1133 if ((m_joystickAxes[stick].m_available & mask) != 0) {
1134 return OptionalDouble.of(m_joystickAxes[stick].m_axes[axis]);
1135 }
1136 } finally {
1137 m_cacheDataMutex.unlock();
1138 }
1139
1140 return OptionalDouble.empty();
1141 }
1142
1150 public static POVDirection getStickPOV(int stick, int pov) {
1151 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1152 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1153 }
1154 if (pov < 0 || pov >= DriverStationJNI.MAX_JOYSTICK_POVS) {
1155 throw new IllegalArgumentException("Joystick POV is out of range");
1156 }
1157
1158 int mask = 1 << pov;
1159
1160 m_cacheDataMutex.lock();
1161 try {
1162 if ((m_joystickPOVs[stick].m_available & mask) != 0) {
1163 return POVDirection.of(m_joystickPOVs[stick].m_povs[pov]);
1164 }
1165 } finally {
1166 m_cacheDataMutex.unlock();
1167 }
1168
1169 reportJoystickResourceWarning(stick, JoystickResourceAlert.POV, pov);
1170 return POVDirection.CENTER;
1171 }
1172
1179 public static long getStickButtons(final int stick) {
1180 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1181 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1182 }
1183
1184 m_cacheDataMutex.lock();
1185 try {
1186 return m_joystickButtons[stick].m_buttons;
1187 } finally {
1188 m_cacheDataMutex.unlock();
1189 }
1190 }
1191
1198 public static int getStickAxesMaximumIndex(int stick) {
1199 return availableToCount(getStickAxesAvailable(stick));
1200 }
1201
1208 public static int getStickAxesAvailable(int stick) {
1209 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1210 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1211 }
1212
1213 m_cacheDataMutex.lock();
1214 try {
1215 return m_joystickAxes[stick].m_available;
1216 } finally {
1217 m_cacheDataMutex.unlock();
1218 }
1219 }
1220
1227 public static int getStickPOVsMaximumIndex(int stick) {
1228 return availableToCount(getStickPOVsAvailable(stick));
1229 }
1230
1237 public static int getStickPOVsAvailable(int stick) {
1238 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1239 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1240 }
1241
1242 m_cacheDataMutex.lock();
1243 try {
1244 return m_joystickPOVs[stick].m_available;
1245 } finally {
1246 m_cacheDataMutex.unlock();
1247 }
1248 }
1249
1256 public static int getStickButtonsMaximumIndex(int stick) {
1257 return availableToCount(getStickButtonsAvailable(stick));
1258 }
1259
1266 public static long getStickButtonsAvailable(int stick) {
1267 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1268 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1269 }
1270
1271 m_cacheDataMutex.lock();
1272 try {
1273 return m_joystickButtons[stick].m_available;
1274 } finally {
1275 m_cacheDataMutex.unlock();
1276 }
1277 }
1278
1285 public static boolean getJoystickIsGamepad(int stick) {
1286 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1287 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1288 }
1289
1290 return DriverStationJNI.getJoystickIsGamepad((byte) stick) == 1;
1291 }
1292
1299 public static int getJoystickGamepadType(int stick) {
1300 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1301 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1302 }
1303
1304 return DriverStationJNI.getJoystickGamepadType((byte) stick);
1305 }
1306
1313 public static int getJoystickSupportedOutputs(int stick) {
1314 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1315 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1316 }
1317
1318 return DriverStationJNI.getJoystickSupportedOutputs((byte) stick);
1319 }
1320
1327 public static String getJoystickName(int stick) {
1328 if (stick < 0 || stick >= JOYSTICK_PORTS) {
1329 throw new IllegalArgumentException("Joystick index is out of range, should be 0-5");
1330 }
1331
1332 return DriverStationJNI.getJoystickName((byte) stick);
1333 }
1334
1344 public static boolean isJoystickConnected(int stick) {
1345 return getStickAxesAvailable(stick) != 0
1346 || getStickButtonsAvailable(stick) != 0
1347 || getStickPOVsAvailable(stick) != 0;
1348 }
1349
1355 public static boolean isEnabled() {
1356 m_cacheDataMutex.lock();
1357 try {
1358 return m_controlWord.isEnabled() && m_controlWord.isDSAttached();
1359 } finally {
1360 m_cacheDataMutex.unlock();
1361 }
1362 }
1363
1369 public static boolean isDisabled() {
1370 return !isEnabled();
1371 }
1372
1378 public static boolean isEStopped() {
1379 m_cacheDataMutex.lock();
1380 try {
1381 return m_controlWord.isEStopped();
1382 } finally {
1383 m_cacheDataMutex.unlock();
1384 }
1385 }
1386
1399 public static RobotMode getRobotMode() {
1400 if (!m_userProgramStarted) {
1401 return RobotMode.UNKNOWN;
1402 }
1403
1404 m_cacheDataMutex.lock();
1405 try {
1406 return m_controlWord.getRobotMode();
1407 } finally {
1408 m_cacheDataMutex.unlock();
1409 }
1410 }
1411
1423 public static boolean isAutonomous() {
1424 return getRobotMode() == RobotMode.AUTONOMOUS;
1425 }
1426
1438 public static boolean isAutonomousEnabled() {
1439 m_cacheDataMutex.lock();
1440 try {
1441 return isAutonomous() && isEnabled();
1442 } finally {
1443 m_cacheDataMutex.unlock();
1444 }
1445 }
1446
1458 public static boolean isTeleop() {
1460 }
1461
1473 public static boolean isTeleopEnabled() {
1474 m_cacheDataMutex.lock();
1475 try {
1476 return isTeleop() && isEnabled();
1477 } finally {
1478 m_cacheDataMutex.unlock();
1479 }
1480 }
1481
1493 public static boolean isUtility() {
1494 return getRobotMode() == RobotMode.UTILITY;
1495 }
1496
1508 public static boolean isUtilityEnabled() {
1509 m_cacheDataMutex.lock();
1510 try {
1511 return isUtility() && isEnabled();
1512 } finally {
1513 m_cacheDataMutex.unlock();
1514 }
1515 }
1516
1517 private static int convertColorToInt(Color color) {
1518 if (color == null) {
1519 return -1;
1520 }
1521 return (((int) (color.red * 255) & 0xff) << 16)
1522 | (((int) (color.green * 255) & 0xff) << 8)
1523 | ((int) (color.blue * 255) & 0xff);
1524 }
1525
1540 @SuppressWarnings("PMD.UseStringBufferForStringAppends")
1541 public static long addOpMode(
1542 RobotMode mode,
1543 String name,
1544 String group,
1545 String description,
1546 Color textColor,
1547 Color backgroundColor) {
1548 if (name.isBlank()) {
1549 throw new IllegalArgumentException("OpMode name must be non-blank");
1550 }
1551 // find unique ID
1552 m_opModesMutex.lock();
1553 try {
1554 String nameCopy = name;
1555 for (; ; ) {
1556 long id = OpModeOption.makeId(mode, nameCopy.hashCode());
1557 OpModeOption existing = m_opModes.get(id);
1558 if (existing == null) {
1559 m_opModes.put(
1560 id,
1561 new OpModeOption(
1562 id,
1563 name,
1564 group,
1565 description,
1566 convertColorToInt(textColor),
1567 convertColorToInt(backgroundColor)));
1568 return id;
1569 }
1570 if (existing.getMode() == mode && existing.name.equals(name)) {
1571 // already exists
1572 throw new IllegalArgumentException("OpMode " + name + " already exists for mode " + mode);
1573 }
1574 // collision, try again with space appended
1575 nameCopy += ' ';
1576 }
1577 } finally {
1578 m_opModesMutex.unlock();
1579 }
1580 }
1581
1594 public static long addOpMode(RobotMode mode, String name, String group, String description) {
1595 return addOpMode(mode, name, group, description, null, null);
1596 }
1597
1609 public static long addOpMode(RobotMode mode, String name, String group) {
1610 return addOpMode(mode, name, group, "");
1611 }
1612
1623 public static long addOpMode(RobotMode mode, String name) {
1624 return addOpMode(mode, name, "");
1625 }
1626
1635 public static long removeOpMode(RobotMode mode, String name) {
1636 if (name.isBlank()) {
1637 return 0;
1638 }
1639 m_opModesMutex.lock();
1640 try {
1641 // we have to loop over all entries to find the one with the correct name
1642 // because the of the unique ID generation scheme
1643 for (OpModeOption opMode : m_opModes.values()) {
1644 if (opMode.getMode() == mode && opMode.name.equals(name)) {
1645 m_opModes.remove(opMode.id);
1646 return opMode.id;
1647 }
1648 }
1649 } finally {
1650 m_opModesMutex.unlock();
1651 }
1652 return 0;
1653 }
1654
1656 public static void publishOpModes() {
1657 m_opModesMutex.lock();
1658 try {
1659 OpModeOption[] options = new OpModeOption[m_opModes.size()];
1660 DriverStationJNI.setOpModeOptions(m_opModes.values().toArray(options));
1661 } finally {
1662 m_opModesMutex.unlock();
1663 }
1664
1665 var modeCounts =
1666 m_opModes.values().stream()
1667 .collect(Collectors.groupingBy(OpModeOption::getMode, Collectors.counting()));
1668
1669 for (RobotMode mode : RobotMode.values()) {
1671 "OpMode/" + mode, String.valueOf(modeCounts.getOrDefault(mode, 0L)));
1672 }
1673 }
1674
1676 public static void clearOpModes() {
1677 m_opModesMutex.lock();
1678 try {
1679 m_opModes.clear();
1680 DriverStationJNI.setOpModeOptions(new OpModeOption[0]);
1681 } finally {
1682 m_opModesMutex.unlock();
1683 }
1684 }
1685
1703 public static void observeUserProgramStarting() {
1704 m_userProgramStarted = true;
1705 DriverStationJNI.observeUserProgramStarting();
1706 }
1707
1715 public static void clearUserProgramStarted() {
1716 if (!RobotBase.isSimulation()) {
1717 // NOP unless running in sim. We don't want teams to call this; it would prevent their
1718 // programs from transitioning between modes and from detecting opmode selections.
1720 "DriverStationBackend.clearUserProgramStarted() was called! "
1721 + "This method exists solely for internal WPILib unit tests "
1722 + "and does nothing in team code.",
1723 true);
1724 return;
1725 }
1726
1727 m_userProgramStarted = false;
1728 }
1729
1744 public static long getOpModeId() {
1745 if (!m_userProgramStarted) {
1746 return 0;
1747 }
1748
1749 m_cacheDataMutex.lock();
1750 try {
1751 return m_controlWord.getOpModeId();
1752 } finally {
1753 m_cacheDataMutex.unlock();
1754 }
1755 }
1756
1771 public static String getOpMode() {
1772 if (!m_userProgramStarted) {
1773 return "";
1774 }
1775
1776 m_cacheDataMutex.lock();
1777 try {
1778 return m_opMode;
1779 } finally {
1780 m_cacheDataMutex.unlock();
1781 }
1782 }
1783
1791 public static boolean isOpMode(long id) {
1792 return getOpModeId() == id;
1793 }
1794
1802 public static boolean isOpMode(String mode) {
1803 return getOpMode().equals(mode);
1804 }
1805
1811 public static boolean isDSAttached() {
1812 m_cacheDataMutex.lock();
1813 try {
1814 return m_controlWord.isDSAttached();
1815 } finally {
1816 m_cacheDataMutex.unlock();
1817 }
1818 }
1819
1825 public static boolean isFMSAttached() {
1826 m_cacheDataMutex.lock();
1827 try {
1828 return m_controlWord.isFMSAttached();
1829 } finally {
1830 m_cacheDataMutex.unlock();
1831 }
1832 }
1833
1841 public static Optional<String> getGameData() {
1842 m_cacheDataMutex.lock();
1843 try {
1844 if (m_gameData == null || m_gameData.isEmpty()) {
1845 return Optional.empty();
1846 }
1847 return Optional.of(m_gameData);
1848 } finally {
1849 m_cacheDataMutex.unlock();
1850 }
1851 }
1852
1858 public static String getEventName() {
1859 m_cacheDataMutex.lock();
1860 try {
1861 return m_matchInfo.eventName;
1862 } finally {
1863 m_cacheDataMutex.unlock();
1864 }
1865 }
1866
1872 public static MatchType getMatchType() {
1873 int matchType;
1874 m_cacheDataMutex.lock();
1875 try {
1876 matchType = m_matchInfo.matchType;
1877 } finally {
1878 m_cacheDataMutex.unlock();
1879 }
1880 return switch (matchType) {
1881 case 1 -> MatchType.PRACTICE;
1882 case 2 -> MatchType.QUALIFICATION;
1883 case 3 -> MatchType.ELIMINATION;
1884 default -> MatchType.NONE;
1885 };
1886 }
1887
1893 public static int getMatchNumber() {
1894 m_cacheDataMutex.lock();
1895 try {
1896 return m_matchInfo.matchNumber;
1897 } finally {
1898 m_cacheDataMutex.unlock();
1899 }
1900 }
1901
1907 public static int getReplayNumber() {
1908 m_cacheDataMutex.lock();
1909 try {
1910 return m_matchInfo.replayNumber;
1911 } finally {
1912 m_cacheDataMutex.unlock();
1913 }
1914 }
1915
1916 private static Map<AllianceStationID, Optional<Alliance>> m_allianceMap =
1917 Map.of(
1918 AllianceStationID.UNKNOWN, Optional.empty(),
1919 AllianceStationID.RED_1, Optional.of(Alliance.RED),
1920 AllianceStationID.RED_2, Optional.of(Alliance.RED),
1921 AllianceStationID.RED_3, Optional.of(Alliance.RED),
1922 AllianceStationID.BLUE_1, Optional.of(Alliance.BLUE),
1923 AllianceStationID.BLUE_2, Optional.of(Alliance.BLUE),
1924 AllianceStationID.BLUE_3, Optional.of(Alliance.BLUE));
1925
1926 private static Map<AllianceStationID, OptionalInt> m_stationMap =
1927 Map.of(
1928 AllianceStationID.UNKNOWN, OptionalInt.empty(),
1929 AllianceStationID.RED_1, OptionalInt.of(1),
1930 AllianceStationID.RED_2, OptionalInt.of(2),
1931 AllianceStationID.RED_3, OptionalInt.of(3),
1932 AllianceStationID.BLUE_1, OptionalInt.of(1),
1933 AllianceStationID.BLUE_2, OptionalInt.of(2),
1934 AllianceStationID.BLUE_3, OptionalInt.of(3));
1935
1943 public static Optional<Alliance> getAlliance() {
1944 AllianceStationID allianceStationID = DriverStationJNI.getAllianceStation();
1945 if (allianceStationID == null) {
1946 allianceStationID = AllianceStationID.UNKNOWN;
1947 }
1948
1949 return m_allianceMap.get(allianceStationID);
1950 }
1951
1959 public static OptionalInt getLocation() {
1960 AllianceStationID allianceStationID = DriverStationJNI.getAllianceStation();
1961 if (allianceStationID == null) {
1962 allianceStationID = AllianceStationID.UNKNOWN;
1963 }
1964
1965 return m_stationMap.get(allianceStationID);
1966 }
1967
1977 return DriverStationJNI.getAllianceStation();
1978 }
1979
1997 public static double getMatchTime() {
1998 return DriverStationJNI.getMatchTime();
1999 }
2000
2007 public static void silenceJoystickConnectionAlert(boolean silence) {
2008 m_joystickAlertMutex.lock();
2009 try {
2010 m_silenceJoystickAlerts = silence;
2011 if (silence && !isFMSAttached()) {
2012 clearJoystickAlertsLocked();
2013 }
2014 } finally {
2015 m_joystickAlertMutex.unlock();
2016 }
2017 }
2018
2025 public static boolean isJoystickConnectionAlertSilenced() {
2026 m_joystickAlertMutex.lock();
2027 try {
2028 return !isFMSAttached() && m_silenceJoystickAlerts;
2029 } finally {
2030 m_joystickAlertMutex.unlock();
2031 }
2032 }
2033
2039 public static void refreshControlWordFromCache(ControlWord word) {
2040 m_cacheDataMutex.lock();
2041 try {
2042 word.update(m_controlWord);
2043 } finally {
2044 m_cacheDataMutex.unlock();
2045 }
2046 }
2047
2052 public static void refreshData() {
2053 DriverStationJNI.refreshDSData();
2054
2055 // Get the status of all the joysticks
2056 for (byte stick = 0; stick < JOYSTICK_PORTS; stick++) {
2057 DriverStationJNI.getAllJoystickData(
2058 stick,
2059 m_joystickAxesCache[stick].m_axes,
2060 m_joystickAxesRawCache[stick].m_axes,
2061 m_joystickPOVsCache[stick].m_povs,
2062 m_touchpadFingersCache,
2063 m_metadataCache);
2064 m_joystickAxesCache[stick].m_available = (int) m_metadataCache[0];
2065 m_joystickAxesRawCache[stick].m_available = (int) m_metadataCache[0];
2066 m_joystickPOVsCache[stick].m_available = (int) m_metadataCache[1];
2067 m_joystickButtonsCache[stick].m_available = m_metadataCache[2];
2068 m_joystickButtonsCache[stick].m_buttons = m_metadataCache[3];
2069 m_joystickTouchpadsCache[stick].m_count = (int) m_metadataCache[4];
2070 for (int i = 0; i < m_joystickTouchpadsCache[stick].m_count; i++) {
2071 long metadata = m_metadataCache[5 + i];
2072 m_joystickTouchpadsCache[stick].m_touchpads[i].m_fingers[0].m_down = (metadata & 0x1) != 0;
2073 m_joystickTouchpadsCache[stick].m_touchpads[i].m_fingers[1].m_down = (metadata & 0x2) != 0;
2074 m_joystickTouchpadsCache[stick].m_touchpads[i].m_count = (int) (metadata >> 2 & 0x3);
2075 for (int j = 0; j < m_joystickTouchpadsCache[stick].m_touchpads[i].m_count; j++) {
2076 m_joystickTouchpadsCache[stick].m_touchpads[i].m_fingers[j].m_x =
2077 m_touchpadFingersCache[i * 4 + j * 2 + 0];
2078 m_joystickTouchpadsCache[stick].m_touchpads[i].m_fingers[j].m_y =
2079 m_touchpadFingersCache[i * 4 + j * 2 + 1];
2080 }
2081 }
2082 }
2083
2084 DriverStationJNI.getMatchInfo(m_matchInfoCache);
2085
2086 // This is a pass through, so if it hasn't changed, it doesn't
2087 // reallocate
2088 m_gameDataCache = DriverStationJNI.getGameData(m_gameDataCache);
2089
2090 DriverStationJNI.getControlWord(m_controlWordCache);
2091
2092 m_opModeCache = opModeToString(m_controlWordCache.getOpModeId());
2093
2094 DataLogSender dataLogSender;
2095 // lock joystick mutex to swap cache data
2096 m_cacheDataMutex.lock();
2097 try {
2098 for (int i = 0; i < JOYSTICK_PORTS; i++) {
2099 // If buttons weren't pressed and are now, set flags in m_buttonsPressed
2100 m_joystickButtonsPressed[i] |=
2101 ~m_joystickButtons[i].m_buttons & m_joystickButtonsCache[i].m_buttons;
2102
2103 // If buttons were pressed and aren't now, set flags in m_buttonsReleased
2104 m_joystickButtonsReleased[i] |=
2105 m_joystickButtons[i].m_buttons & ~m_joystickButtonsCache[i].m_buttons;
2106 }
2107
2108 // move cache to actual data
2109 HALJoystickAxes[] currentAxes = m_joystickAxes;
2110 m_joystickAxes = m_joystickAxesCache;
2111 m_joystickAxesCache = currentAxes;
2112
2113 HALJoystickAxesRaw[] currentAxesRaw = m_joystickAxesRaw;
2114 m_joystickAxesRaw = m_joystickAxesRawCache;
2115 m_joystickAxesRawCache = currentAxesRaw;
2116
2117 HALJoystickButtons[] currentButtons = m_joystickButtons;
2118 m_joystickButtons = m_joystickButtonsCache;
2119 m_joystickButtonsCache = currentButtons;
2120
2121 HALJoystickPOVs[] currentPOVs = m_joystickPOVs;
2122 m_joystickPOVs = m_joystickPOVsCache;
2123 m_joystickPOVsCache = currentPOVs;
2124
2125 HALJoystickTouchpads[] currentTouchpads = m_joystickTouchpads;
2126 m_joystickTouchpads = m_joystickTouchpadsCache;
2127 m_joystickTouchpadsCache = currentTouchpads;
2128
2129 MatchInfoData currentInfo = m_matchInfo;
2130 m_matchInfo = m_matchInfoCache;
2131 m_matchInfoCache = currentInfo;
2132
2133 m_gameData = m_gameDataCache;
2134
2135 ControlWord currentWord = m_controlWord;
2136 m_controlWord = m_controlWordCache;
2137 m_controlWordCache = currentWord;
2138
2139 String currentOpMode = m_opMode;
2140 m_opMode = m_opModeCache;
2141 m_opModeCache = currentOpMode;
2142
2143 dataLogSender = m_dataLogSender;
2144 } finally {
2145 m_cacheDataMutex.unlock();
2146 }
2147
2148 clearJoystickAlerts();
2149
2150 m_refreshEvents.wakeup();
2151
2152 m_matchDataSender.sendMatchData();
2153 if (dataLogSender != null) {
2154 dataLogSender.send(WPIUtilJNI.now());
2155 }
2156 }
2157
2163 public static void provideRefreshedDataEventHandle(int handle) {
2164 m_refreshEvents.add(handle);
2165 }
2166
2172 public static void removeRefreshedDataEventHandle(int handle) {
2173 m_refreshEvents.remove(handle);
2174 }
2175
2176 private static JoystickAlerts getJoystickAlertsLocked(int stick) {
2177 JoystickAlerts alerts = m_joystickAlerts[stick];
2178 if (alerts == null) {
2179 alerts = new JoystickAlerts(stick);
2180 m_joystickAlerts[stick] = alerts;
2181 }
2182 return alerts;
2183 }
2184
2185 private static void reportJoystickResourceWarning(
2186 int stick, JoystickResourceAlert resourceAlert, int resource) {
2187 m_joystickAlertMutex.lock();
2188 try {
2189 if (!isFMSAttached() && m_silenceJoystickAlerts) {
2190 return;
2191 }
2192
2193 JoystickAlerts alerts = getJoystickAlertsLocked(stick);
2194 if (!alerts.prepareForReport()) {
2195 return;
2196 }
2197
2198 alerts.reportResourceWarning(resourceAlert, resource);
2199 } finally {
2200 m_joystickAlertMutex.unlock();
2201 }
2202 }
2203
2204 private static void reportJoystickTouchpadFingerWarning(int stick, int touchpad, int finger) {
2205 m_joystickAlertMutex.lock();
2206 try {
2207 if (!isFMSAttached() && m_silenceJoystickAlerts) {
2208 return;
2209 }
2210
2211 JoystickAlerts alerts = getJoystickAlertsLocked(stick);
2212 if (!alerts.prepareForReport()) {
2213 return;
2214 }
2215
2216 alerts.reportTouchpadFingerWarning(touchpad, finger);
2217 } finally {
2218 m_joystickAlertMutex.unlock();
2219 }
2220 }
2221
2222 private static void clearJoystickAlerts() {
2223 m_joystickAlertMutex.lock();
2224 try {
2225 clearJoystickAlertsLocked();
2226 } finally {
2227 m_joystickAlertMutex.unlock();
2228 }
2229 }
2230
2231 private static void clearJoystickAlertsLocked() {
2232 if (!isFMSAttached() && m_silenceJoystickAlerts) {
2233 for (JoystickAlerts alerts : m_joystickAlerts) {
2234 if (alerts != null) {
2235 alerts.clearAllAlerts();
2236 }
2237 }
2238 return;
2239 }
2240
2241 for (JoystickAlerts alerts : m_joystickAlerts) {
2242 if (alerts != null) {
2243 alerts.refresh();
2244 }
2245 }
2246 }
2247
2254 @SuppressWarnings("PMD.NonThreadSafeSingleton")
2255 public static void startDataLog(DataLog log, boolean logJoysticks) {
2256 m_cacheDataMutex.lock();
2257 try {
2258 if (m_dataLogSender == null) {
2259 m_dataLogSender = new DataLogSender(log, logJoysticks, WPIUtilJNI.now());
2260 }
2261 } finally {
2262 m_cacheDataMutex.unlock();
2263 }
2264 }
2265
2272 public static void startDataLog(DataLog log) {
2273 startDataLog(log, true);
2274 }
2275}
TouchpadFinger(boolean down, float x, float y)
static TouchpadFinger getStickTouchpadFinger(int stick, int touchpad, int finger)
static void reportError(String error, StackTraceElement[] stackTrace)
static Optional< Boolean > getStickButtonIfAvailable(final int stick, final int button)
static boolean getStickButtonReleased(final int stick, final int button)
static long addOpMode(RobotMode mode, String name, String group, String description)
static OptionalDouble getStickAxisIfAvailable(int stick, int axis)
static void reportCrash(String error, StackTraceElement[] stackTrace)
static void reportWarning(String warning, StackTraceElement[] stackTrace)
static long addOpMode(RobotMode mode, String name, String group, String description, Color textColor, Color backgroundColor)
static boolean getStickButton(final int stick, final int button)
static boolean getStickTouchpadFingerAvailable(int stick, int touchpad, int finger)
static boolean getStickButtonPressed(final int stick, final int button)
static void startDataLog(DataLog log, boolean logJoysticks)
static long addOpMode(RobotMode mode, String name, String group)
static void reportWarning(String warning, boolean printTrace)
static void reportError(String error, boolean printTrace)
void update(long opModeHash, RobotMode robotMode, boolean enabled, boolean emergencyStop, boolean fmsAttached, boolean dsAttached)
final double blue
Definition Color.java:22
final double red
Definition Color.java:16
static void reportUsage(String resource, String data)
static POVDirection of(byte value)