[hal,wpilib] Add support for joystick outputs (#8385)

Support joystick outputs, including Rumble and LEDs.

Also requires an update to Joystick descriptors, as that has also
changed in mrccomm to support showing what outputs are supported.
This commit is contained in:
Thad House
2025-11-17 14:36:14 -08:00
committed by GitHub
parent 5db6d2f500
commit ce6fd225a6
54 changed files with 1607 additions and 854 deletions

View File

@@ -2,6 +2,8 @@
// 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 <utility>
#include "wpi/hal/proto/ControlData.h"
#include "wpi/util/protobuf/ProtobufCallbacks.hpp"
@@ -85,7 +87,7 @@ std::optional<mrc::ControlData> wpi::util::Protobuf<mrc::ControlData>::Unpack(
ControlData.SetJoystickCount(Joysticks.size());
for (size_t i = 0; i < ControlData.GetJoystickCount(); i++) {
ControlData.Joysticks()[i] = Joysticks[i];
ControlData.Joysticks()[i] = std::move(Joysticks[i]);
}
return ControlData;

View File

@@ -1,69 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// 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 <utility>
#include "wpi/hal/proto/JoystickDescriptor.h"
#include "wpi/util/protobuf/ProtobufCallbacks.hpp"
std::optional<mrc::JoystickDescriptor>
wpi::util::Protobuf<mrc::JoystickDescriptor>::Unpack(InputStream& Stream) {
wpi::util::UnpackCallback<std::string> JoystickNameCb;
wpi::util::UnpackCallback<uint8_t, MRC_MAX_NUM_AXES> AxisTypesCb;
mrc_proto_ProtobufJoystickDescriptor Msg;
Msg.JoystickName = JoystickNameCb.Callback();
Msg.AxisTypes = AxisTypesCb.Callback();
if (!Stream.Decode(Msg)) {
return {};
}
auto JoystickName = JoystickNameCb.Items();
auto AxisTypes = AxisTypesCb.Items();
if (JoystickName.empty()) {
return {};
}
mrc::JoystickDescriptor OutputData;
OutputData.MoveName(std::move(JoystickName[0]));
OutputData.SetAxesCount(AxisTypes.size());
for (size_t i = 0; i < OutputData.GetAxesCount(); i++) {
OutputData.AxesTypes()[i] = AxisTypes[i];
}
OutputData.SetPovsCount(Msg.PovCount);
OutputData.SetButtonsCount(Msg.ButtonCount);
OutputData.IsGamepad = Msg.IsGamepad ? 1 : 0;
OutputData.Type = Msg.JoystickType;
OutputData.RumbleCount = Msg.RumbleCount;
return OutputData;
}
bool wpi::util::Protobuf<mrc::JoystickDescriptor>::Pack(
OutputStream& Stream, const mrc::JoystickDescriptor& Value) {
std::string_view JoystickName = Value.GetName();
wpi::util::PackCallback JoystickNameCb{&JoystickName};
std::span<const uint8_t> AxisTypes = Value.AxesTypes();
wpi::util::PackCallback AxisTypesCb{AxisTypes};
mrc_proto_ProtobufJoystickDescriptor Msg{
.JoystickName = JoystickNameCb.Callback(),
.AxisTypes = AxisTypesCb.Callback(),
.IsGamepad = Value.IsGamepad ? true : false,
.JoystickType = Value.Type,
.ButtonCount = static_cast<int32_t>(Value.GetButtonsCount()),
.PovCount = static_cast<int32_t>(Value.GetPovsCount()),
.RumbleCount = Value.RumbleCount,
};
return Stream.Encode(Msg);
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) FIRST and other WPILib contributors.
// 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 <utility>
#include "wpi/hal/proto/JoystickDescriptors.h"
#include "wpi/util/protobuf/ProtobufCallbacks.hpp"
std::optional<mrc::JoystickDescriptors>
wpi::util::Protobuf<mrc::JoystickDescriptors>::Unpack(InputStream& Stream) {
wpi::util::UnpackCallback<mrc::JoystickDescriptor, MRC_MAX_NUM_JOYSTICKS>
JoystickDescriptorsCb;
mrc_proto_ProtobufJoystickDescriptors Msg;
Msg.Descriptors = JoystickDescriptorsCb.Callback();
if (!Stream.Decode(Msg)) {
return {};
}
auto Descriptors = JoystickDescriptorsCb.Items();
mrc::JoystickDescriptors OutputData;
OutputData.SetDescriptorCount(Descriptors.size());
for (size_t i = 0; i < OutputData.GetDescriptorCount(); i++) {
OutputData.Descriptors()[i] = std::move(Descriptors[i]);
}
return OutputData;
}
bool wpi::util::Protobuf<mrc::JoystickDescriptors>::Pack(
OutputStream& Stream, const mrc::JoystickDescriptors& Value) {
std::span<const mrc::JoystickDescriptor> Descriptors = Value.Descriptors();
wpi::util::PackCallback JoystickDescriptorsCb{Descriptors};
mrc_proto_ProtobufJoystickDescriptors Msg{
.Descriptors = JoystickDescriptorsCb.Callback(),
};
return Stream.Encode(Msg);
}
std::optional<mrc::JoystickDescriptor>
wpi::util::Protobuf<mrc::JoystickDescriptor>::Unpack(InputStream& Stream) {
wpi::util::UnpackCallback<std::string> JoystickNameCb;
mrc_proto_ProtobufJoystickDescriptor Msg;
Msg.JoystickName = JoystickNameCb.Callback();
if (!Stream.Decode(Msg)) {
return {};
}
auto JoystickName = JoystickNameCb.Items();
mrc::JoystickDescriptor OutputData;
if (!JoystickName.empty()) {
OutputData.MoveName(std::move(JoystickName[0]));
}
OutputData.IsGamepad = Msg.IsGamepad ? 1 : 0;
OutputData.GamepadType = Msg.GamepadType;
OutputData.SupportedOutputs = Msg.SupportedOutputs;
return OutputData;
}
bool wpi::util::Protobuf<mrc::JoystickDescriptor>::Pack(
OutputStream& Stream, const mrc::JoystickDescriptor& Value) {
std::string_view JoystickName = Value.GetName();
wpi::util::PackCallback JoystickNameCb{&JoystickName};
mrc_proto_ProtobufJoystickDescriptor Msg{
.JoystickName = JoystickNameCb.Callback(),
.IsGamepad = Value.IsGamepad ? true : false,
.GamepadType = Value.GamepadType,
.SupportedOutputs = Value.SupportedOutputs,
};
return Stream.Encode(Msg);
}

View File

@@ -0,0 +1,79 @@
// Copyright (c) FIRST and other WPILib contributors.
// 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 <utility>
#include "wpi/hal/proto/JoystickOutput.h"
#include "wpi/util/protobuf/ProtobufCallbacks.hpp"
std::optional<mrc::JoystickOutputs>
wpi::util::Protobuf<mrc::JoystickOutputs>::Unpack(InputStream& Stream) {
wpi::util::UnpackCallback<mrc::JoystickOutput, MRC_MAX_NUM_JOYSTICKS>
JoystickOutputsCb;
mrc_proto_ProtobufJoystickOutputs Msg;
Msg.Outputs = JoystickOutputsCb.Callback();
if (!Stream.Decode(Msg)) {
return {};
}
auto Outputs = JoystickOutputsCb.Items();
mrc::JoystickOutputs OutputData;
OutputData.SetOutputCount(Outputs.size());
for (size_t i = 0; i < OutputData.GetOutputCount(); i++) {
OutputData.Outputs()[i] = std::move(Outputs[i]);
}
return OutputData;
}
bool wpi::util::Protobuf<mrc::JoystickOutputs>::Pack(
OutputStream& Stream, const mrc::JoystickOutputs& Value) {
std::span<const mrc::JoystickOutput> Outputs = Value.Outputs();
wpi::util::PackCallback JoystickOutputsCb{Outputs};
mrc_proto_ProtobufJoystickOutputs Msg{
.Outputs = JoystickOutputsCb.Callback(),
};
return Stream.Encode(Msg);
}
std::optional<mrc::JoystickOutput>
wpi::util::Protobuf<mrc::JoystickOutput>::Unpack(InputStream& Stream) {
mrc_proto_ProtobufJoystickOutput Msg;
if (!Stream.Decode(Msg)) {
return {};
}
return mrc::JoystickOutput{
.R = static_cast<uint8_t>((Msg.LEDs >> 16) & 0xFF),
.G = static_cast<uint8_t>((Msg.LEDs >> 8) & 0xFF),
.B = static_cast<uint8_t>(Msg.LEDs & 0xFF),
.LeftRumble = static_cast<uint16_t>((Msg.Rumble >> 16) & 0xFFFF),
.RightRumble = static_cast<uint16_t>(Msg.Rumble & 0xFFFF),
.LeftTriggerRumble =
static_cast<uint16_t>((Msg.TriggerRumble >> 16) & 0xFFFF),
.RightTriggerRumble = static_cast<uint16_t>(Msg.TriggerRumble & 0xFFFF),
};
}
bool wpi::util::Protobuf<mrc::JoystickOutput>::Pack(
OutputStream& Stream, const mrc::JoystickOutput& Value) {
mrc_proto_ProtobufJoystickOutput Msg{
.LEDs = (static_cast<uint32_t>(Value.R) << 16) |
(static_cast<uint32_t>(Value.G) << 8) |
static_cast<uint32_t>(Value.B),
.Rumble = (static_cast<uint32_t>(Value.LeftRumble) << 16) |
static_cast<uint32_t>(Value.RightRumble),
.TriggerRumble = (static_cast<uint32_t>(Value.LeftTriggerRumble) << 16) |
static_cast<uint32_t>(Value.RightTriggerRumble),
};
return Stream.Encode(Msg);
}

View File

@@ -1,41 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// 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 "wpi/hal/proto/JoystickRumbleData.h"
#include "wpi/util/protobuf/ProtobufCallbacks.hpp"
std::optional<mrc::JoystickRumbleData>
wpi::util::Protobuf<mrc::JoystickRumbleData>::Unpack(InputStream& Stream) {
wpi::util::UnpackCallback<uint16_t, MRC_MAX_NUM_RUMBLE> RumbleCb;
mrc_proto_ProtobufJoystickRumbleData Msg{
.Value = RumbleCb.Callback(),
};
if (!Stream.Decode(Msg)) {
return {};
}
auto Rumbles = RumbleCb.Items();
mrc::JoystickRumbleData Rumble;
Rumble.SetCount(Rumbles.size());
for (size_t i = 0; i < Rumble.GetCount(); i++) {
Rumble.Rumbles()[i] = Rumbles[i];
}
return Rumble;
}
bool wpi::util::Protobuf<mrc::JoystickRumbleData>::Pack(
OutputStream& Stream, const mrc::JoystickRumbleData& Value) {
wpi::util::PackCallback RumbleCb{Value.Rumbles()};
mrc_proto_ProtobufJoystickRumbleData Msg{
.Value = RumbleCb.Callback(),
};
return Stream.Encode(Msg);
}