Skip to content

Commit 9f7db9b

Browse files
GrayHoangpre-commit-ci-lite[bot]nycrat
authored
Keyboard support for robot diagnostics mode in Thunderscope (#3738)
* keyboard support * [pre-commit.ci lite] apply automatic fixes * refactor * [pre-commit.ci lite] apply automatic fixes * pygame * requirementslock * [pre-commit.ci lite] apply automatic fixes * Remove outdated pyserial reference * Revert "Remove outdated pyserial reference" This reverts commit 8421b5b. * Add back pyserial requirement for estop helper * Regenerate requirements lock * Rename to IControllerBase * Make code consistent with docs * Get rid of unnecessary placeholders --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Avah Xiao <avahxiao@gmail.com>
1 parent 43493f2 commit 9f7db9b

10 files changed

Lines changed: 455 additions & 289 deletions

File tree

src/software/thunderscope/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package(default_visibility = ["//visibility:public"])
66
compile_pip_requirements(
77
name = "requirements",
88
src = "requirements.in",
9-
requirements_darwin = "requirements_lock.darwin.txt",
109
requirements_txt = "requirements_lock.txt",
1110
)
1211

src/software/thunderscope/constants.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,6 @@ class TrailValues:
327327
class DiagnosticsConstants:
328328
"""Constants for Robot Diagnostics"""
329329

330-
# Device names of the controllers supported for controlling robots
331-
SUPPORTED_CONTROLLERS = {
332-
"Microsoft Xbox One X pad",
333-
"Microsoft X-Box One S pad",
334-
"Microsoft X-Box 360 pad",
335-
"Microsoft Xbox 360 pad",
336-
"Generic X-Box pad",
337-
}
338-
339330
BUTTON_PRESSED_THRESHOLD = 0.5
340331
DEADZONE_PERCENTAGE = 0.20
341332

src/software/thunderscope/requirements.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
colorama==0.4.6
22
netifaces==0.11.0
3-
evdev==1.7.0; sys_platform == "linux"
43
numpy==1.26.4
54
protobuf==6.31.1
65
pyserial==3.5
6+
pygame==2.6.1
77
pyqtgraph==0.13.7
88
pyqtdarktheme-fork==2.3.2
99
PyQt6-Qt6==6.8.1

src/software/thunderscope/requirements_lock.darwin.txt

Lines changed: 0 additions & 136 deletions
This file was deleted.

src/software/thunderscope/requirements_lock.txt

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ darkdetect==0.7.1 \
1212
--hash=sha256:3efe69f8ecd5f1b7f4fbb0d1d93f656b0e493c45cc49222380ffe2a529cbc866 \
1313
--hash=sha256:47be3cf5134432ddb616bbffc927237718407914993c82809983e7ccebf49013
1414
# via pyqtdarktheme-fork
15-
evdev==1.7.0 ; sys_platform == "linux" \
16-
--hash=sha256:95bd2a1e0c6ce2cd7a2ecc6e6cd9736ff794b3ad5cb54d81d8cbc2e414d0b870
17-
# via -r software/thunderscope/requirements.in
1815
netifaces==0.11.0 \
1916
--hash=sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32 \
2017
--hash=sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea \
@@ -102,6 +99,68 @@ protobuf==6.31.1 \
10299
--hash=sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39 \
103100
--hash=sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a
104101
# via -r software/thunderscope/requirements.in
102+
pygame==2.6.1 \
103+
--hash=sha256:00827aba089355925902d533f9c41e79a799641f03746c50a374dc5c3362e43d \
104+
--hash=sha256:10e3d2a55f001f6c0a6eb44aa79ea7607091c9352b946692acedb2ac1482f1c9 \
105+
--hash=sha256:1206125f14cae22c44565c9d333607f1d9f59487b1f1432945dfc809aeaa3e88 \
106+
--hash=sha256:14f9dda45469b254c0f15edaaeaa85d2cc072ff6a83584a265f5d684c7f7efd8 \
107+
--hash=sha256:15efaa11a80a65dd589a95bebe812fa5bfc7e14946b638a424c5bd9ac6cca1a4 \
108+
--hash=sha256:163e66de169bd5670c86e27d0b74aad0d2d745e3b63cf4e7eb5b2bff1231ca8d \
109+
--hash=sha256:173badf82fa198e6888017bea40f511cb28e69ecdd5a72b214e81e4dcd66c3b1 \
110+
--hash=sha256:17498a2b043bc0e795faedef1b081199c688890200aef34991c1941caa2d2c89 \
111+
--hash=sha256:20349195326a5e82a16e351ed93465a7845a7e2a9af55b7bc1b2110ea3e344e1 \
112+
--hash=sha256:21160d9093533eb831f1b708e630706e5ac16b30750571ec27bc3b8364814f38 \
113+
--hash=sha256:27eb17e3dc9640e4b4683074f1890e2e879827447770470c2aba9f125f74510b \
114+
--hash=sha256:28b43190436037e428a5be28fc80cf6615304fd528009f2c688cc828f4ff104b \
115+
--hash=sha256:2a3a1288e2e9b1e5834e425bedd5ba01a3cd4902b5c2bff8ed4a740ccfe98171 \
116+
--hash=sha256:2a615d78b2364e86f541458ff41c2a46181b9a1e9eabd97b389282fdf04efbb3 \
117+
--hash=sha256:325a84d072d52e3c2921eff02f87c6a74b7e77d71db3bdf53801c6c975f1b6c4 \
118+
--hash=sha256:33006f784e1c7d7e466fcb61d5489da59cc5f7eb098712f792a225df1d4e229d \
119+
--hash=sha256:3a9e7396be0d9633831c3f8d5d82dd63ba373ad65599628294b7a4f8a5a01a65 \
120+
--hash=sha256:3acd8c009317190c2bfd81db681ecef47d5eb108c2151d09596d9c7ea9df5c0e \
121+
--hash=sha256:3bede70ec708057e305815d6546012669226d1d80566785feca9b044216062e7 \
122+
--hash=sha256:481cfe1bdbb7fe00acc5950c494c26f00240888619bdc396fc8c39a734797432 \
123+
--hash=sha256:4a8ea113b1bf627322a025a1a5a87e3818a7f55ab3a4077ff1ae5c8c60576614 \
124+
--hash=sha256:4c1623180e70a03c4a734deb9bac50fc9c82942ae84a3a220779062128e75f3b \
125+
--hash=sha256:4ee7f2771f588c966fa2fa8b829be26698c9b4836f82ede5e4edc1a68594942e \
126+
--hash=sha256:56fb02ead529cee00d415c3e007f75e0780c655909aaa8e8bf616ee09c9feb1f \
127+
--hash=sha256:56ffca6059b165bbf64f4b4be23b8068f6a0e220780e4f96ec0bb5ac3c63ec39 \
128+
--hash=sha256:5d09fd950725d187aa5207c0cb8eb9ab0d2f8ce9ab8d189c30eeb470e71b617e \
129+
--hash=sha256:6582aa71a681e02e55d43150a9ab41394e6bf4d783d2962a10aea58f424be060 \
130+
--hash=sha256:7103c60939bbc1e05cfc7ba3f1d2ad3bbf103b7828b82a7166a9ab6f51950146 \
131+
--hash=sha256:7bffdd3eaf394d9645331d1c3a5df9d782ebcc3c5a78f3b657c7879a828dd111 \
132+
--hash=sha256:811e7b925146d8149d79193652cbb83e0eca0aae66476b1cb310f0f4226b8b5c \
133+
--hash=sha256:813af4fba5d0b2cb8e58f5d95f7910295c34067dcc290d34f1be59c48bd1ea6a \
134+
--hash=sha256:816e85000c5d8b02a42b9834f761a5925ef3377d2924e3a7c4c143d2990ce5b8 \
135+
--hash=sha256:818b4eaec9c4acb6ac64805d4ca8edd4062bebca77bd815c18739fe2842c97e9 \
136+
--hash=sha256:84fc4054e25262140d09d39e094f6880d730199710829902f0d8ceae0213379e \
137+
--hash=sha256:8a78fd030d98faab4a8e27878536fdff7518d3e062a72761c552f624ebba5a5f \
138+
--hash=sha256:91476902426facd4bb0dad4dc3b2573bc82c95c71b135e0daaea072ed528d299 \
139+
--hash=sha256:94afd1177680d92f9214c54966ad3517d18210c4fbc5d84a0192d218e93647e0 \
140+
--hash=sha256:97ac4e13847b6b293ecaffa5ffce9886c98d09c03309406931cc592f0cea6366 \
141+
--hash=sha256:9beeb647e555afb5657111fa83acb74b99ad88761108eaea66472e8b8547b55b \
142+
--hash=sha256:9dd5c054d4bd875a8caf978b82672f02bec332f52a833a76899220c460bb4b58 \
143+
--hash=sha256:a1bf7ab5311bbced70320f1a56701650b4c18231343ae5af42111eea91e0949a \
144+
--hash=sha256:a4b8f04fceddd9a3ac30778d11f0254f59efcd1c382d5801271113cea8b4f2f3 \
145+
--hash=sha256:a620883d589926f157b8f1d1f543183ac52e5c30507dea445e3927ae0bee1c54 \
146+
--hash=sha256:ac3f033d2be4a9e23660a96afe2986df3a6916227538a6a0061bc218c5088507 \
147+
--hash=sha256:ae6039f3a55d800db80e8010f387557b528d34d534435e0871326804df2a62f2 \
148+
--hash=sha256:b46e68cd168f44d0224c670bb72186688fc692d7079715f79d04096757d703d0 \
149+
--hash=sha256:b7f9f8e6f76de36f4725175d686601214af362a4f30614b4dae2240198e72e6f \
150+
--hash=sha256:bbb7167c92103a2091366e9af26d4914ba3776666e8677d3c93551353fffa626 \
151+
--hash=sha256:c0b11356ac96261162d54a2c2b41a41978f00525631b01ec9c4fe26b01c66595 \
152+
--hash=sha256:c31dbdb5d0217f32764797d21c2752e258e5fb7e895326538d82b5f75a0cd856 \
153+
--hash=sha256:c47a6938de93fa610accd4969e638c2aebcb29b2fca518a84c3a39d91ab47116 \
154+
--hash=sha256:c8040ea2ab18c6b255af706ec01355c8a6b08dc48d77fd4ee783f8fc46a843bf \
155+
--hash=sha256:ce8cc108b92de9b149b344ad2e25eedbe773af0dc41dfb24d1f07f679b558c60 \
156+
--hash=sha256:d1a7f2b66ac2e4c9583b6d4c6d6f346fb10a3392c04163f537061f86a448ed5c \
157+
--hash=sha256:d29eb9a93f12aa3d997b6e3c447ac85b2a4b142ab2548441523a8fcf5e216042 \
158+
--hash=sha256:da3ad64d685f84a34ebe5daacb39fff14f1251acb34c098d760d63fee768f50c \
159+
--hash=sha256:ef07c0103d79492c21fced9ad68c11c32efa6801ca1920ebfd0f15fb46c78b1c \
160+
--hash=sha256:f3935459109da4bb0b3901da9904f0a3e52028a3332a355d298b1673a334cf21 \
161+
--hash=sha256:f84f15d146d6aa93254008a626c56ef96fed276006202881a47b29757f0cd65a \
162+
--hash=sha256:fb6e8d0547f30ddc845f4fd1e33070ef548233ad0dbf21f7ecea768883d1bbdc
163+
# via -r software/thunderscope/requirements.in
105164
pyqt-toast-notification==1.3.2 \
106165
--hash=sha256:135736ec0f16bff41104dee3c60ac318e5d55ae3378bf26892c6d08c36088ae6 \
107166
--hash=sha256:82688101202737736d51ab6c74a573b32266ecb7c8b0002f913407bd369737d9

src/software/thunderscope/robot_diagnostics/BUILD

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,41 @@ py_library(
2323
],
2424
)
2525

26+
py_library(
27+
name = "keyboard_controller",
28+
srcs = ["keyboard_controller.py"],
29+
deps = [
30+
":controller_base",
31+
"//software/thunderscope:constants",
32+
requirement("pyqtgraph"),
33+
],
34+
)
35+
2636
py_library(
2737
name = "handheld_controller",
2838
srcs = ["handheld_controller.py"],
39+
deps = [
40+
":controller_base",
41+
"//software/thunderscope:constants",
42+
requirement("pygame"),
43+
],
44+
)
45+
46+
py_library(
47+
name = "controller_base",
48+
srcs = ["controller_base.py"],
2949
)
3050

3151
py_library(
3252
name = "handheld_controller_widget",
3353
srcs = ["handheld_controller_widget.py"],
3454
deps = [
55+
":controller_base",
3556
":handheld_controller",
57+
":keyboard_controller",
3658
"//software/thunderscope:constants",
3759
requirement("pyqtgraph"),
38-
] + select({
39-
# TODO: remove this selection when we replace evdev to
40-
# other macos supported libs.
41-
"@platforms//os:linux": [requirement("evdev")],
42-
"//conditions:default": [],
43-
}),
60+
],
4461
)
4562

4663
py_library(
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from abc import ABC, abstractmethod
2+
3+
4+
class IControllerBase(ABC):
5+
"""Abstract base class for controller input sources."""
6+
7+
@abstractmethod
8+
def name(self) -> str:
9+
"""Get the display name of the input source."""
10+
11+
@abstractmethod
12+
def connected(self) -> bool:
13+
"""Return true if the input source is active and available."""
14+
15+
@abstractmethod
16+
def close(self) -> None:
17+
"""Release any resources held by the input source."""
18+
19+
def update(self) -> None:
20+
"""Refresh controller input state. Called once per frame before reading inputs.
21+
Override for polled backends; push-based controllers can leave this as a no-op.
22+
"""
23+
24+
@abstractmethod
25+
def get_move_velocity(self) -> tuple[float, float, float]:
26+
"""Return (x, y, angular) velocity, each normalized to [-1, 1] with deadzone applied.
27+
Positive x = forward, positive y = strafe left, positive angular = CCW.
28+
"""
29+
30+
@abstractmethod
31+
def get_speed_factor(self) -> float:
32+
"""Return 1.0 normally, or SPEED_SLOWDOWN_FACTOR when slowdown input is active."""
33+
34+
@abstractmethod
35+
def is_dribbler_held(self) -> bool:
36+
"""Return True if the dribbler engage input is active."""
37+
38+
@abstractmethod
39+
def get_kick_power_step(self) -> int:
40+
"""Return -1, 0, or +1 for kick/chip power step direction.
41+
Non-zero only once per new input (edge-detected).
42+
"""
43+
44+
@abstractmethod
45+
def get_dribbler_step(self) -> int:
46+
"""Return -1, 0, or +1 for dribbler RPM step direction.
47+
Non-zero only once per new input (edge-detected).
48+
"""
49+
50+
@abstractmethod
51+
def is_kick_fired(self) -> bool:
52+
"""Return True once per kick button press (rising edge only)."""
53+
54+
@abstractmethod
55+
def is_chip_fired(self) -> bool:
56+
"""Return True once per chip button press (rising edge only)."""

0 commit comments

Comments
 (0)