[wpilib,cmd] Cache HID wrappers (#8970)

Store DriverStation-owned GenericHID and Gamepad instances in Java and
C++, and expose the cached objects to Python bindings.

Move hand-written command gamepad and joystick wrappers to compose
cached CommandGenericHID instances plus typed HID wrappers, including a
Python CommandGamepad.

This will let us remove UserControls, while helping ensure that we don't
have state smashing between GenericHID objects.

Another bonus is without inheritance, intellisense will no longer show a
bunch of annoying methods, and instead just what actually exists.

---------

Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
This commit is contained in:
Thad House
2026-06-11 09:42:39 -07:00
committed by GitHub
parent fe499ede4c
commit c647e67de0
105 changed files with 4210 additions and 1336 deletions

View File

@@ -2,7 +2,7 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <string>
#include <cstring>
#include <tuple>
#include <gtest/gtest.h>
@@ -37,7 +37,7 @@ INSTANTIATE_TEST_SUITE_P(IsConnectedTests, IsJoystickConnectedParametersTest,
std::make_tuple(4, 10, 1, true)));
class JoystickConnectionWarningTest
: public ::testing::TestWithParam<
std::tuple<bool, bool, bool, std::string>> {};
std::tuple<bool, bool, bool, const char*>> {};
TEST_P(JoystickConnectionWarningTest, JoystickConnectionWarnings) {
// Capture all output to stderr.
@@ -51,15 +51,17 @@ TEST_P(JoystickConnectionWarningTest, JoystickConnectionWarnings) {
// Create joystick and attempt to retrieve button.
wpi::Joystick joystick(0);
joystick.GetRawButton(1);
joystick.GetHID().GetRawButton(1);
wpi::sim::StepTiming(1_s);
EXPECT_EQ(wpi::internal::DriverStationBackend::
IsJoystickConnectionWarningSilenced(),
std::get<2>(GetParam()));
EXPECT_EQ(::testing::internal::GetCapturedStderr().substr(
0, std::get<3>(GetParam()).size()),
std::get<3>(GetParam()));
auto expected = std::get<3>(GetParam());
EXPECT_STREQ(::testing::internal::GetCapturedStderr()
.substr(0, std::strlen(expected))
.c_str(),
expected);
}
INSTANTIATE_TEST_SUITE_P(

View File

@@ -6,13 +6,14 @@
#include <gtest/gtest.h>
#include "wpi/driverstation/internal/DriverStationBackend.hpp"
#include "wpi/simulation/GenericHIDSim.hpp"
using namespace wpi;
using RumbleType = GenericHID::RumbleType;
static constexpr double kEpsilon = 0.0001;
TEST(GenericHIDTest, RumbleRange) {
GenericHID hid{0};
GenericHID hid = internal::DriverStationBackend::ConstructGenericHID(0);
sim::GenericHIDSim sim{0};
for (int i = 0; i <= 100; i++) {
@@ -34,7 +35,7 @@ TEST(GenericHIDTest, RumbleRange) {
}
TEST(GenericHIDTest, RumbleTypes) {
GenericHID hid{0};
GenericHID hid = internal::DriverStationBackend::ConstructGenericHID(0);
sim::GenericHIDSim sim{0};
// Make sure all are off

View File

@@ -0,0 +1,67 @@
import pytest
from wpilib import DriverStation, DriverStationBackend, GenericHID
from wpilib.simulation import DriverStationSim, GenericHIDSim
RumbleType = GenericHID.RumbleType
RUMBLE_TYPES = (
RumbleType.LEFT_RUMBLE,
RumbleType.RIGHT_RUMBLE,
RumbleType.LEFT_TRIGGER_RUMBLE,
RumbleType.RIGHT_TRIGGER_RUMBLE,
)
@pytest.fixture(autouse=True)
def reset_driver_station_sim():
DriverStationSim.resetData()
yield
DriverStationBackend.resetCachedHIDData()
DriverStationSim.resetData()
def test_rumble_range() -> None:
hid = DriverStationBackend.constructGenericHID(0)
sim = GenericHIDSim(0)
for i in range(101):
rumble_value = i / 100.0
for rumble_type in RUMBLE_TYPES:
hid.setRumble(rumble_type, rumble_value)
assert sim.getRumble(rumble_type) == pytest.approx(
rumble_value, abs=0.0001
)
def test_rumble_types() -> None:
hid = DriverStationBackend.constructGenericHID(0)
sim = GenericHIDSim(0)
for rumble_type in RUMBLE_TYPES:
hid.setRumble(rumble_type, 0)
for rumble_type in RUMBLE_TYPES:
assert sim.getRumble(rumble_type) == pytest.approx(0, abs=0.0001)
for active_rumble_type in RUMBLE_TYPES:
hid.setRumble(active_rumble_type, 1)
for rumble_type in RUMBLE_TYPES:
expected = 1 if rumble_type == active_rumble_type else 0
assert sim.getRumble(rumble_type) == pytest.approx(
expected, abs=0.0001
)
hid.setRumble(active_rumble_type, 0)
def test_cached_hid_data_reset() -> None:
DriverStation.getGenericHID(0)
DriverStation.getGamepad(0)
DriverStationBackend.resetCachedHIDData()
assert DriverStation.getGenericHID(0).getPort() == 0
assert DriverStation.getGamepad(0).getPort() == 0