Files
allwpilib/wpilibc/src/main/native/cpp/hardware/expansionhub/ExpansionHubMotor.cpp
Thad House 3f1cf3cabe [wpilib] ExHub Follower Fixes (#8892)
A chunk of updates to followers on Expansion Hub

Fixes followers not implicitly enabling.
Allows followers to follow other followers
Throws exceptions on construction if a follower cycle is detected.
Allows reversing followers.

The enable thing will fix
https://github.com/wpilibsuite/SystemcoreTesting/discussions/259#discussioncomment-16886195

Closes #8843

Depends on https://github.com/wpilibsuite/scservices/pull/30
2026-05-14 21:50:38 -07:00

186 lines
5.9 KiB
C++

// 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/hardware/expansionhub/ExpansionHubMotor.hpp"
#include "wpi/hardware/expansionhub/ExpansionHubPositionConstants.hpp"
#include "wpi/hardware/expansionhub/ExpansionHubVelocityConstants.hpp"
#include "wpi/system/Errors.hpp"
#include "wpi/system/SystemServer.hpp"
static constexpr int kPercentageMode = 0;
static constexpr int kVoltageMode = 1;
static constexpr int kPositionMode = 2;
static constexpr int kVelocityMode = 3;
static constexpr int kFollowerMode = 4;
using namespace wpi;
ExpansionHubMotor::ExpansionHubMotor(int usbId, int channel)
: m_hub{usbId},
m_channel{channel},
m_velocityConstants{usbId, channel},
m_positionConstants{usbId, channel} {
WPILIB_AssertMessage(channel >= 0 && channel < ExpansionHub::NumMotorPorts,
"ExHub Motor Channel {} out of range", channel);
if (!m_hub.CheckAndReserveMotor(channel)) {
throw WPILIB_MakeError(err::ResourceAlreadyAllocated, "Channel {}",
channel);
}
m_hub.ReportUsage(fmt::format("ExHubMotor[{}]", channel), "ExHubMotor");
auto systemServer = SystemServer::GetSystemServer();
wpi::nt::PubSubOptions options;
options.sendAll = true;
options.keepDuplicates = true;
options.periodic = 0.005;
m_encoderSubscriber = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/encoder", usbId, channel))
.Subscribe(0, options);
m_encoderVelocitySubscriber =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/encoderVelocity", usbId, channel))
.Subscribe(0, options);
m_currentSubscriber = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/current", usbId, channel))
.Subscribe(0, options);
m_setpointPublisher = systemServer
.GetDoubleTopic(fmt::format(
"/rhsp/{}/motor{}/setpoint", usbId, channel))
.Publish(options);
m_distancePerCountPublisher =
systemServer
.GetDoubleTopic(
fmt::format("/rhsp/{}/motor{}/distancePerCount", usbId, channel))
.Publish(options);
m_floatOn0Publisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/floatOn0", usbId, channel))
.Publish(options);
m_enabledPublisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/enabled", usbId, channel))
.Publish(options);
m_modePublisher =
systemServer
.GetIntegerTopic(fmt::format("/rhsp/{}/motor{}/mode", usbId, channel))
.Publish(options);
m_reversedPublisher = systemServer
.GetBooleanTopic(fmt::format(
"/rhsp/{}/motor{}/reversed", usbId, channel))
.Publish(options);
m_resetEncoderPublisher =
systemServer
.GetBooleanTopic(
fmt::format("/rhsp/{}/motor{}/resetEncoder", usbId, channel))
.Publish(options);
}
ExpansionHubMotor::~ExpansionHubMotor() noexcept {
m_hub.UnreserveMotor(m_channel);
}
void ExpansionHubMotor::SetThrottle(double throttle) {
SetEnabled(true);
m_modePublisher.Set(kPercentageMode);
m_setpointPublisher.Set(throttle);
}
void ExpansionHubMotor::SetVoltage(wpi::units::volt_t voltage) {
SetEnabled(true);
m_modePublisher.Set(kVoltageMode);
m_setpointPublisher.Set(voltage.value());
}
void ExpansionHubMotor::SetPositionSetpoint(double setpoint) {
SetEnabled(true);
m_modePublisher.Set(kPositionMode);
m_setpointPublisher.Set(setpoint);
}
void ExpansionHubMotor::SetVelocitySetpoint(double setpoint) {
SetEnabled(true);
m_modePublisher.Set(kVelocityMode);
m_setpointPublisher.Set(setpoint);
}
void ExpansionHubMotor::SetEnabled(bool enabled) {
m_enabledPublisher.Set(enabled);
}
void ExpansionHubMotor::SetFloatOn0(bool floatOn0) {
m_floatOn0Publisher.Set(floatOn0);
}
wpi::units::ampere_t ExpansionHubMotor::GetCurrent() const {
return wpi::units::ampere_t{m_currentSubscriber.Get(0)};
}
void ExpansionHubMotor::SetDistancePerCount(double perCount) {
m_distancePerCountPublisher.Set(perCount);
}
double ExpansionHubMotor::GetEncoderVelocity() const {
return m_encoderVelocitySubscriber.Get(0);
}
double ExpansionHubMotor::GetEncoderPosition() const {
return m_encoderSubscriber.Get(0);
}
void ExpansionHubMotor::SetReversed(bool reversed) {
m_reversedPublisher.Set(true);
}
void ExpansionHubMotor::ResetEncoder() {
m_resetEncoderPublisher.Set(true);
}
ExpansionHubVelocityConstants& ExpansionHubMotor::GetVelocityConstants() {
return m_velocityConstants;
}
ExpansionHubPositionConstants& ExpansionHubMotor::GetPositionConstants() {
return m_positionConstants;
}
void ExpansionHubMotor::Follow(const ExpansionHubMotor& leader,
FollowDirection direction) {
if (m_hub.GetUsbId() != leader.m_hub.GetUsbId()) {
throw WPILIB_MakeError(err::InvalidParameter,
"Cannot follow motor on different hub");
}
if (m_channel == leader.m_channel) {
throw WPILIB_MakeError(err::InvalidParameter, "Cannot follow self");
}
m_hub.AddFollower(leader.m_channel, m_channel);
SetEnabled(true);
m_modePublisher.Set(kFollowerMode);
if (direction == FollowDirection::Opposed) {
m_setpointPublisher.Set(leader.m_channel + 4);
} else {
m_setpointPublisher.Set(leader.m_channel);
}
}
void ExpansionHubMotor::Unfollow() {
m_hub.RemoveFollower(m_channel);
SetEnabled(false);
m_modePublisher.Set(kPercentageMode);
m_setpointPublisher.Set(0);
}