[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

@@ -8,28 +8,42 @@
{%- endmacro %}
#include "wpi/driverstation/{{ ConsoleName }}Controller.hpp"
#include "wpi/driverstation/DriverStation.hpp"
#include "wpi/event/BooleanEvent.hpp"
#include "wpi/hal/UsageReporting.hpp"
#include "wpi/util/sendable/SendableBuilder.hpp"
#include "wpi/event/BooleanEvent.hpp"
using namespace wpi;
{{ ConsoleName }}Controller::{{ ConsoleName }}Controller(int port) : GenericHID(port) {
HAL_ReportUsage("HID", port, "{{ ConsoleName }}Controller");
{{ ConsoleName }}Controller::{{ ConsoleName }}Controller(int port)
: {{ ConsoleName }}Controller{DriverStation::GetGenericHID(port)} {}
{{ ConsoleName }}Controller::{{ ConsoleName }}Controller(GenericHID& hid)
: m_hid{&hid} {
HAL_ReportUsage("HID", hid.GetPort(), "{{ ConsoleName }}Controller");
}
GenericHID& {{ ConsoleName }}Controller::GetHID() {
return *m_hid;
}
const GenericHID& {{ ConsoleName }}Controller::GetHID() const {
return *m_hid;
}
{% for stick in sticks %}
double {{ ConsoleName }}Controller::Get{{ stick.NameParts|map("capitalize")|join }}() const {
return GetRawAxis(Axis::k{{ stick.NameParts|map("capitalize")|join }});
return m_hid->GetRawAxis(Axis::k{{ stick.NameParts|map("capitalize")|join }});
}
{% endfor -%}
{% for trigger in triggers %}
double {{ ConsoleName }}Controller::Get{{ capitalize_first(trigger.name) }}Axis() const {
return GetRawAxis(Axis::k{{ capitalize_first(trigger.name) }});
return m_hid->GetRawAxis(Axis::k{{ capitalize_first(trigger.name) }});
}
{% if trigger.UseThresholdMethods %}
BooleanEvent {{ ConsoleName }}Controller::{{ capitalize_first(trigger.name) }}(double threshold, EventLoop* loop) const {
return BooleanEvent(loop, [this, threshold] { return this->Get{{ capitalize_first(trigger.name) }}Axis() > threshold; });
BooleanEvent {{ ConsoleName }}Controller::{{ capitalize_first(trigger.name) }}(double threshold,
EventLoop* loop) const {
return BooleanEvent(
loop, [this, threshold] { return this->Get{{ capitalize_first(trigger.name) }}Axis() > threshold; });
}
BooleanEvent {{ ConsoleName }}Controller::{{ capitalize_first(trigger.name) }}(EventLoop* loop) const {
@@ -39,31 +53,59 @@ BooleanEvent {{ ConsoleName }}Controller::{{ capitalize_first(trigger.name) }}(E
{% endfor -%}
{% for button in buttons %}
bool {{ ConsoleName }}Controller::Get{{ capitalize_first(button.name) }}Button() const {
return GetRawButton(Button::k{{ capitalize_first(button.name) }});
return m_hid->GetRawButton(Button::k{{ capitalize_first(button.name) }});
}
bool {{ ConsoleName }}Controller::Get{{ capitalize_first(button.name) }}ButtonPressed() {
return GetRawButtonPressed(Button::k{{ capitalize_first(button.name) }});
return m_hid->GetRawButtonPressed(Button::k{{ capitalize_first(button.name) }});
}
bool {{ ConsoleName }}Controller::Get{{ capitalize_first(button.name) }}ButtonReleased() {
return GetRawButtonReleased(Button::k{{ capitalize_first(button.name) }});
return m_hid->GetRawButtonReleased(Button::k{{ capitalize_first(button.name) }});
}
BooleanEvent {{ ConsoleName }}Controller::{{ capitalize_first(button.name) }}(EventLoop* loop) const {
return BooleanEvent(loop, [this]() { return this->Get{{ capitalize_first(button.name) }}Button(); });
}
{% endfor %}
bool {{ ConsoleName }}Controller::IsConnected() const {
return m_hid->IsConnected();
}
GenericHID::HIDType {{ ConsoleName }}Controller::GetGamepadType() const {
return m_hid->GetGamepadType();
}
GenericHID::SupportedOutputs {{ ConsoleName }}Controller::GetSupportedOutputs() const {
return m_hid->GetSupportedOutputs();
}
std::string {{ ConsoleName }}Controller::GetName() const {
return m_hid->GetName();
}
int {{ ConsoleName }}Controller::GetPort() const {
return m_hid->GetPort();
}
void {{ ConsoleName }}Controller::SetRumble(GenericHID::RumbleType type,
double value) {
m_hid->SetRumble(type, value);
}
void {{ ConsoleName }}Controller::InitSendable(wpi::util::SendableBuilder& builder) {
builder.SetSmartDashboardType("HID");
builder.PublishConstString("ControllerType", "{{ ConsoleName }}");
{%- for trigger in triggers %}
builder.AddDoubleProperty("{{ capitalize_first(trigger.name) }} Axis", [this] { return Get{{ capitalize_first(trigger.name) }}Axis(); }, nullptr);
builder.AddDoubleProperty("{{ capitalize_first(trigger.name) }} Axis",
[this] { return Get{{ capitalize_first(trigger.name) }}Axis(); }, nullptr);
{%- endfor -%}
{% for stick in sticks %}
builder.AddDoubleProperty("{{ stick.NameParts|map("capitalize")|join }}", [this] { return Get{{ stick.NameParts|map("capitalize")|join }}(); }, nullptr);
builder.AddDoubleProperty("{{ stick.NameParts|map("capitalize")|join }}",
[this] { return Get{{ stick.NameParts|map("capitalize")|join }}(); }, nullptr);
{%- endfor -%}
{% for button in buttons %}
builder.AddBooleanProperty("{{ capitalize_first(button.name) }}", [this] { return Get{{ capitalize_first(button.name) }}Button(); }, nullptr);
builder.AddBooleanProperty("{{ capitalize_first(button.name) }}",
[this] { return Get{{ capitalize_first(button.name) }}Button(); }, nullptr);
{%- endfor %}
}

View File

@@ -14,7 +14,7 @@ using namespace wpi;
using namespace wpi::sim;
{{ ConsoleName }}ControllerSim::{{ ConsoleName }}ControllerSim(const {{ ConsoleName }}Controller& joystick)
: GenericHIDSim{joystick} {
: GenericHIDSim{joystick.GetHID()} {
SetAxesMaximumIndex({{ sticks|length + triggers|length }});
SetButtonsMaximumIndex({{ buttons|length }});
SetPOVsMaximumIndex(1);

View File

@@ -8,10 +8,12 @@
{%- endmacro %}
#pragma once
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
#include <string>
#include "wpi/driverstation/GenericHID.hpp"
#include "wpi/driverstation/HIDDevice.hpp"
#include "wpi/util/sendable/Sendable.hpp"
#include "wpi/util/sendable/SendableHelper.hpp"
namespace wpi {
@@ -27,9 +29,10 @@ namespace wpi {
* correct mapping, and only through the official NI DS. Sim is not guaranteed
* to have the same mapping, as well as any 3rd party controllers.
*/
class {{ ConsoleName }}Controller : public GenericHID,
public wpi::util::Sendable,
public wpi::util::SendableHelper<{{ ConsoleName }}Controller> {
class {{ ConsoleName }}Controller
: public HIDDevice,
public wpi::util::Sendable,
public wpi::util::SendableHelper<{{ ConsoleName }}Controller> {
public:
/**
* Construct an instance of a controller.
@@ -41,10 +44,31 @@ class {{ ConsoleName }}Controller : public GenericHID,
*/
explicit {{ ConsoleName }}Controller(int port);
/**
* Construct an instance of a controller with a GenericHID object.
*
* @param hid The GenericHID object to use for this controller.
*/
explicit {{ ConsoleName }}Controller(GenericHID& hid);
~{{ ConsoleName }}Controller() override = default;
{{ ConsoleName }}Controller({{ ConsoleName }}Controller&&) = default;
{{ ConsoleName }}Controller& operator=({{ ConsoleName }}Controller&&) = default;
/**
* Get the underlying GenericHID object.
*
* @return the wrapped GenericHID object
*/
GenericHID& GetHID() override;
/**
* Get the underlying GenericHID object.
*
* @return the wrapped GenericHID object
*/
const GenericHID& GetHID() const override;
{% for stick in sticks %}
/**
* Get the {{ stick.NameParts[1] }} axis value of {{ stick.NameParts[0] }} side of the controller. {{ stick.PositiveDirection }} is positive.
@@ -137,7 +161,56 @@ class {{ ConsoleName }}Controller : public GenericHID,
{%- endfor %}
};
/**
* Get if the controller is connected.
*
* @return true if the controller is connected
*/
bool IsConnected() const;
/**
* Get the type of the controller.
*
* @return the type of the controller.
*/
GenericHID::HIDType GetGamepadType() const;
/**
* Get the supported outputs of the controller.
*
* @return the supported outputs of the controller.
*/
GenericHID::SupportedOutputs GetSupportedOutputs() const;
/**
* Get the name of the controller.
*
* @return the name of the controller.
*/
std::string GetName() const;
/**
* Get the port number of the controller.
*
* @return The port number of the controller.
*/
int GetPort() const;
/**
* Set the rumble output for the HID.
*
* The DS currently supports 4 rumble values: left rumble, right rumble, left
* trigger rumble, and right trigger rumble.
*
* @param type Which rumble value to set
* @param value The normalized value (0 to 1) to set the rumble to
*/
void SetRumble(GenericHID::RumbleType type, double value);
void InitSendable(wpi::util::SendableBuilder& builder) override;
private:
GenericHID* m_hid;
};
} // namespace wpi