001/*
002 * Copyright (C) Cross The Road Electronics.  All rights reserved.
003 * License information can be found in CTRE_LICENSE.txt
004 * For support and suggestions contact support@ctr-electronics.com or file
005 * an issue tracker at https://github.com/CrossTheRoadElec/Phoenix-Releases
006 */
007package com.ctre.phoenix6.mechanisms;
008
009import java.util.Optional;
010import java.util.OptionalInt;
011import java.util.concurrent.atomic.AtomicBoolean;
012import java.util.function.BooleanSupplier;
013
014import edu.wpi.first.units.measure.*;
015import edu.wpi.first.wpilibj.DriverStation;
016
017import static edu.wpi.first.units.Units.*;
018
019import com.ctre.phoenix6.BaseStatusSignal;
020import com.ctre.phoenix6.CANBus;
021import com.ctre.phoenix6.StatusCode;
022import com.ctre.phoenix6.StatusSignal;
023import com.ctre.phoenix6.configs.DifferentialSensorsConfigs;
024import com.ctre.phoenix6.configs.MotorOutputConfigs;
025import com.ctre.phoenix6.configs.TalonFXConfiguration;
026import com.ctre.phoenix6.configs.TalonFXSConfiguration;
027import com.ctre.phoenix6.controls.DifferentialFollower;
028import com.ctre.phoenix6.controls.NeutralOut;
029import com.ctre.phoenix6.controls.CoastOut;
030import com.ctre.phoenix6.controls.StaticBrake;
031import com.ctre.phoenix6.hardware.CANcoder;
032import com.ctre.phoenix6.hardware.CANdi;
033import com.ctre.phoenix6.hardware.Pigeon2;
034import com.ctre.phoenix6.hardware.TalonFX;
035import com.ctre.phoenix6.hardware.TalonFXS;
036import com.ctre.phoenix6.hardware.traits.CommonTalon;
037import com.ctre.phoenix6.mechanisms.DifferentialMotorConstants.*;
038import com.ctre.phoenix6.signals.DifferentialSensorSourceValue;
039import com.ctre.phoenix6.signals.InvertedValue;
040import com.ctre.phoenix6.signals.MotorAlignmentValue;
041import com.ctre.phoenix6.signals.NeutralModeValue;
042
043import com.ctre.phoenix6.controls.DifferentialDutyCycle;
044import com.ctre.phoenix6.controls.DifferentialMotionMagicDutyCycle;
045import com.ctre.phoenix6.controls.DifferentialMotionMagicExpoDutyCycle;
046import com.ctre.phoenix6.controls.DifferentialMotionMagicExpoVoltage;
047import com.ctre.phoenix6.controls.DifferentialMotionMagicVelocityDutyCycle;
048import com.ctre.phoenix6.controls.DifferentialMotionMagicVelocityVoltage;
049import com.ctre.phoenix6.controls.DifferentialMotionMagicVoltage;
050import com.ctre.phoenix6.controls.DifferentialPositionDutyCycle;
051import com.ctre.phoenix6.controls.DifferentialPositionVoltage;
052import com.ctre.phoenix6.controls.DifferentialVelocityDutyCycle;
053import com.ctre.phoenix6.controls.DifferentialVelocityVoltage;
054import com.ctre.phoenix6.controls.DifferentialVoltage;
055
056/**
057 * Manages control of a simple two-axis differential mechanism.
058 * <p>
059 * This mechanism provides limited differential functionality. Pro users
060 * on a CAN FD bus can use the {@link DifferentialMechanism} instead
061 * for full functionality.
062 * <p>
063 * A differential mechanism has two axes of motion, where the position
064 * along each axis is determined by two motors in separate gearboxes:
065 * <ul>
066 *   <li>Driving both motors in a common direction causes the mechanism
067 *       to move forward/reverse, up/down, etc.
068 *   <ul>
069 *     <li>This is the Average axis: position is determined by the
070 *         average of the two motors' positions.
071 *   </ul>
072 *   <li>Driving the motors in opposing directions causes the mechanism
073 *       to twist or rotate left/right.
074 *   <ul>
075 *     <li>This is the Difference axis: rotation is determined by half
076 *         the difference of the two motors' positions.
077 *   </ul>
078 * </ul>
079 */
080public class SimpleDifferentialMechanism<MotorT extends CommonTalon> {
081    /**
082     * Functional interface for device constructors.
083     */
084    @FunctionalInterface
085    public static interface DeviceConstructor<DeviceT> {
086        DeviceT create(int id, CANBus canbus);
087    }
088
089    /**
090     * Possible reasons for the mechanism to disable.
091     */
092    public enum DisabledReasonValue {
093        /**
094         * No reason given.
095         */
096        None,
097        /**
098         * A remote sensor is not present on CAN Bus.
099         */
100        MissingRemoteSensor,
101        /**
102         * The remote Talon FX used for differential
103         * control is not present on CAN Bus.
104         */
105        MissingDifferentialFX,
106        /**
107         * A remote sensor position has overflowed. Because of the nature
108         * of remote sensors, it is possible for a remote sensor position
109         * to overflow beyond what is supported by the status signal frame.
110         * However, this is rare and cannot occur over the course of an FRC
111         * match under normal use.
112         */
113        RemoteSensorPosOverflow,
114        /**
115         * A device or remote sensor has reset.
116         */
117        DeviceHasReset,
118    }
119
120    /**
121     * Possible reasons for the mechanism to require
122     * user action to resume control.
123     */
124    public enum RequiresUserReasonValue {
125        /**
126         * No reason given.
127         */
128        None,
129        /**
130         * A remote sensor position has overflowed. Because of the nature
131         * of remote sensors, it is possible for a remote sensor position
132         * to overflow beyond what is supported by the status signal frame.
133         * However, this is rare and cannot occur over the course of an FRC
134         * match under normal use.
135         */
136        RemoteSensorPosOverflow,
137        /**
138         * A device or remote sensor has reset.
139         */
140        DeviceHasReset,
141    }
142
143    /** Number of times to attempt config applies. */
144    private static final int kNumConfigAttempts = 2;
145
146    private final MotorT _diffLeaderFX;
147    private final MotorT _diffFollowerFX;
148
149    private final double _sensorToDiffRatio;
150
151    private final BooleanSupplier _diffLeaderFXResetChecker;
152    private final BooleanSupplier _diffFollowerFXResetChecker;
153    private final Optional<BooleanSupplier> _diffSensorResetChecker;
154
155    private final DifferentialFollower _diffFollow;
156
157    private final NeutralOut _neutral = new NeutralOut();
158    private final CoastOut _coast = new CoastOut();
159    private final StaticBrake _brake = new StaticBrake();
160
161    private final AtomicBoolean _mechanismDisabled = new AtomicBoolean(false);
162    private final AtomicBoolean _requiresUserAction = new AtomicBoolean(false);
163
164    private DisabledReasonValue _disabledReason = DisabledReasonValue.None;
165    private RequiresUserReasonValue _requiresUserReason = RequiresUserReasonValue.None;
166
167    /**
168     * Creates a new simple differential mechanism using two {@link CommonTalon} devices.
169     * The mechanism will use the average of the two Talon FX sensors on the primary axis,
170     * and half of the difference between the two Talon FX sensors on the differential axis.
171     * <p>
172     * This mechanism provides limited differential functionality. Pro users on a CAN FD
173     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
174     *
175     * @param motorConstructor Constructor for the motors, such as {@code TalonFX::new}
176     * @param constants Constants used to construct the mechanism
177     */
178    public SimpleDifferentialMechanism(DeviceConstructor<MotorT> motorConstructor, DifferentialMotorConstants<?> constants) {
179        final var canbus = new CANBus(constants.CANBusName);
180        _diffLeaderFX = motorConstructor.create(constants.LeaderId, canbus);
181        _diffFollowerFX = motorConstructor.create(constants.FollowerId, canbus);
182        _sensorToDiffRatio = constants.SensorToDifferentialRatio;
183        _diffLeaderFXResetChecker = _diffLeaderFX.getResetOccurredChecker();
184        _diffFollowerFXResetChecker = _diffFollowerFX.getResetOccurredChecker();
185        _diffFollow = new DifferentialFollower(_diffLeaderFX.getDeviceID(), constants.Alignment);
186        _diffSensorResetChecker = Optional.empty();
187
188        applyConfigs(constants, DifferentialSensorSourceValue.RemoteTalonFX_HalfDiff, OptionalInt.empty());
189        /* Set update frequency of relevant signals */
190        BaseStatusSignal.setUpdateFrequencyForAll(
191            constants.ClosedLoopRate,
192            _diffLeaderFX.getDifferentialOutput(false),
193            _diffFollowerFX.getPosition(false),
194            _diffFollowerFX.getVelocity(false)
195        );
196    }
197
198    /**
199     * Creates a new simple differential mechanism using two {@link CommonTalon} devices and
200     * a {@link Pigeon2}. The mechanism will use the average of the two Talon FX sensors on the
201     * primary axis, and the selected Pigeon 2 sensor source on the differential axis.
202     * <p>
203     * This mechanism provides limited differential functionality. Pro users on a CAN FD
204     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
205     *
206     * @param motorConstructor Constructor for the motors, such as {@code TalonFX::new}
207     * @param constants Constants used to construct the mechanism
208     * @param pigeon2 The Pigeon 2 to use for the differential axis
209     * @param pigeonSource The sensor source to use for the Pigeon 2 (Yaw, Pitch, or Roll)
210     */
211    public SimpleDifferentialMechanism(DeviceConstructor<MotorT> motorConstructor, DifferentialMotorConstants<?> constants, Pigeon2 pigeon2, DifferentialPigeon2Source pigeonSource) {
212        final var canbus = new CANBus(constants.CANBusName);
213        _diffLeaderFX = motorConstructor.create(constants.LeaderId, canbus);
214        _diffFollowerFX = motorConstructor.create(constants.FollowerId, canbus);
215        _sensorToDiffRatio = constants.SensorToDifferentialRatio;
216        _diffLeaderFXResetChecker = _diffLeaderFX.getResetOccurredChecker();
217        _diffFollowerFXResetChecker = _diffFollowerFX.getResetOccurredChecker();
218        _diffSensorResetChecker = Optional.of(pigeon2.getResetOccurredChecker());
219        _diffFollow = new DifferentialFollower(_diffLeaderFX.getDeviceID(), constants.Alignment);
220
221        DifferentialSensorSourceValue diffSensorSource;
222        switch (pigeonSource) {
223            case Yaw:
224            default:
225                diffSensorSource = DifferentialSensorSourceValue.RemotePigeon2Yaw;
226                break;
227            case Pitch:
228                diffSensorSource = DifferentialSensorSourceValue.RemotePigeon2Pitch;
229                break;
230            case Roll:
231                diffSensorSource = DifferentialSensorSourceValue.RemotePigeon2Roll;
232                break;
233        }
234        applyConfigs(constants, diffSensorSource, OptionalInt.of(pigeon2.getDeviceID()));
235
236        /* Set update frequency of relevant signals */
237        BaseStatusSignal.setUpdateFrequencyForAll(
238            constants.ClosedLoopRate,
239            _diffLeaderFX.getDifferentialOutput(false),
240            _diffFollowerFX.getPosition(false),
241            _diffFollowerFX.getVelocity(false)
242        );
243        switch (pigeonSource) {
244            case Yaw:
245                pigeon2.getYaw(false).setUpdateFrequency(constants.ClosedLoopRate);
246                break;
247            case Pitch:
248                pigeon2.getPitch(false).setUpdateFrequency(constants.ClosedLoopRate);
249                break;
250            case Roll:
251                pigeon2.getRoll(false).setUpdateFrequency(constants.ClosedLoopRate);
252                break;
253        }
254    }
255
256    /**
257     * Creates a new simple differential mechanism using two {@link CommonTalon} devices and
258     * a {@link CANcoder}. The mechanism will use the average of the two Talon FX sensors on the
259     * primary axis, and the CANcoder position/velocity on the differential axis.
260     * <p>
261     * This mechanism provides limited differential functionality. Pro users on a CAN FD
262     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
263     *
264     * @param motorConstructor Constructor for the motors, such as {@code TalonFX::new}
265     * @param constants Constants used to construct the mechanism
266     * @param cancoder The CANcoder to use for the differential axis
267     */
268    public SimpleDifferentialMechanism(DeviceConstructor<MotorT> motorConstructor, DifferentialMotorConstants<?> constants, CANcoder cancoder) {
269        final var canbus = new CANBus(constants.CANBusName);
270        _diffLeaderFX = motorConstructor.create(constants.LeaderId, canbus);
271        _diffFollowerFX = motorConstructor.create(constants.FollowerId, canbus);
272        _sensorToDiffRatio = constants.SensorToDifferentialRatio;
273        _diffLeaderFXResetChecker = _diffLeaderFX.getResetOccurredChecker();
274        _diffFollowerFXResetChecker = _diffFollowerFX.getResetOccurredChecker();
275        _diffSensorResetChecker = Optional.of(cancoder.getResetOccurredChecker());
276        _diffFollow = new DifferentialFollower(_diffLeaderFX.getDeviceID(), constants.Alignment);
277
278        applyConfigs(constants, DifferentialSensorSourceValue.RemoteCANcoder, OptionalInt.of(cancoder.getDeviceID()));
279        /* Set update frequency of relevant signals */
280        BaseStatusSignal.setUpdateFrequencyForAll(
281            constants.ClosedLoopRate,
282            _diffLeaderFX.getDifferentialOutput(false),
283            _diffFollowerFX.getPosition(false),
284            _diffFollowerFX.getVelocity(false),
285            cancoder.getPosition(false),
286            cancoder.getVelocity(false)
287        );
288    }
289
290    /**
291     * Creates a new simple differential mechanism using two hardware#traits#CommonTalon devices and a
292     * {@link CANdi}. The mechanism will use the average of the two Talon FX sensors on the primary
293     * axis, and the selected CTR Electronics' CANdi™ branded sensor source on the differential axis.
294     * <p>
295     * This mechanism provides limited differential functionality. Pro users on a CAN FD
296     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
297     *
298     * @param motorConstructor Constructor for the motors, such as {@code TalonFX::new}
299     * @param constants Constants used to construct the mechanism
300     * @param candi The CTR Electronics' CANdi™ branded device to use for the differential axis
301     * @param candiSource The sensor source to use for the CTR Electronics' CANdi™ branded device
302     */
303    public SimpleDifferentialMechanism(DeviceConstructor<MotorT> motorConstructor, DifferentialMotorConstants<?> constants, CANdi candi, DifferentialCANdiSource candiSource) {
304        final var canbus = new CANBus(constants.CANBusName);
305        _diffLeaderFX = motorConstructor.create(constants.LeaderId, canbus);
306        _diffFollowerFX = motorConstructor.create(constants.FollowerId, canbus);
307        _sensorToDiffRatio = constants.SensorToDifferentialRatio;
308        _diffLeaderFXResetChecker = _diffLeaderFX.getResetOccurredChecker();
309        _diffFollowerFXResetChecker = _diffFollowerFX.getResetOccurredChecker();
310        _diffSensorResetChecker = Optional.of(candi.getResetOccurredChecker());
311        _diffFollow = new DifferentialFollower(_diffLeaderFX.getDeviceID(), constants.Alignment);
312
313        DifferentialSensorSourceValue diffSensorSource;
314        switch (candiSource) {
315            case PWM1:
316            default:
317                diffSensorSource = DifferentialSensorSourceValue.RemoteCANdiPWM1;
318                break;
319            case PWM2:
320                diffSensorSource = DifferentialSensorSourceValue.RemoteCANdiPWM2;
321                break;
322            case Quadrature:
323                diffSensorSource = DifferentialSensorSourceValue.RemoteCANdiQuadrature;
324                break;
325        }
326        applyConfigs(constants, diffSensorSource, OptionalInt.of(candi.getDeviceID()));
327
328        /* Set update frequency of relevant signals */
329        BaseStatusSignal.setUpdateFrequencyForAll(
330            constants.ClosedLoopRate,
331            _diffLeaderFX.getDifferentialOutput(false),
332            _diffFollowerFX.getPosition(false),
333            _diffFollowerFX.getVelocity(false)
334        );
335        switch (candiSource) {
336            case PWM1:
337                BaseStatusSignal.setUpdateFrequencyForAll(
338                    constants.ClosedLoopRate,
339                    candi.getPWM1Position(false),
340                    candi.getPWM1Velocity(false)
341                );
342                break;
343            case PWM2:
344                BaseStatusSignal.setUpdateFrequencyForAll(
345                    constants.ClosedLoopRate,
346                    candi.getPWM2Position(false),
347                    candi.getPWM2Velocity(false)
348                );
349                break;
350            case Quadrature:
351                BaseStatusSignal.setUpdateFrequencyForAll(
352                    constants.ClosedLoopRate,
353                    candi.getQuadraturePosition(false),
354                    candi.getQuadratureVelocity(false)
355                );
356                break;
357        }
358    }
359
360    private void applyConfigs(DifferentialMotorConstants<?> constants, DifferentialSensorSourceValue diffSensorSource, OptionalInt diffSensorID) {
361        /*
362         * The onboard differential controller adds the differential output to its own output
363         * and subtracts the differential output for differential followers, so use _diffLeaderFX
364         * as the primary controller.
365         */
366
367        InvertedValue leaderInvert;
368
369        /* set up differential control on _diffLeaderFX */
370        {
371            var diff_cfg = new DifferentialSensorsConfigs();
372
373            diff_cfg.DifferentialTalonFXSensorID = _diffFollowerFX.getDeviceID();
374            diff_cfg.DifferentialSensorSource = diffSensorSource;
375            diffSensorID.ifPresent(id -> diff_cfg.DifferentialRemoteSensorID = id);
376            diff_cfg.SensorToDifferentialRatio = constants.SensorToDifferentialRatio;
377
378            StatusCode response = StatusCode.OK;
379
380            if (
381                _diffLeaderFX instanceof TalonFX diffLeaderFX &&
382                (constants.LeaderInitialConfigs == null || constants.LeaderInitialConfigs instanceof TalonFXConfiguration)
383            ) {
384                var leaderConfigs = (TalonFXConfiguration)constants.LeaderInitialConfigs;
385                if (leaderConfigs == null) {
386                    leaderConfigs = new TalonFXConfiguration();
387                }
388                leaderInvert = leaderConfigs.MotorOutput.Inverted;
389
390                leaderConfigs.DifferentialSensors = diff_cfg;
391                for (int i = 0; i < kNumConfigAttempts; ++i) {
392                    response = diffLeaderFX.getConfigurator().apply(leaderConfigs);
393                    if (response.isOK()) break;
394                }
395            } else if (
396                _diffLeaderFX instanceof TalonFXS diffLeaderFXS &&
397                (constants.LeaderInitialConfigs == null || constants.LeaderInitialConfigs instanceof TalonFXSConfiguration)
398            ) {
399                var leaderConfigs = (TalonFXSConfiguration)constants.LeaderInitialConfigs;
400                if (leaderConfigs == null) {
401                    leaderConfigs = new TalonFXSConfiguration();
402                }
403                leaderInvert = leaderConfigs.MotorOutput.Inverted;
404
405                leaderConfigs.DifferentialSensors = diff_cfg;
406                for (int i = 0; i < kNumConfigAttempts; ++i) {
407                    response = diffLeaderFXS.getConfigurator().apply(leaderConfigs);
408                    if (response.isOK()) break;
409                }
410            } else {
411                DriverStation.reportError(
412                    "[phoenix] DifferentialMechanism motor type does not match the DifferentialMotorConstants initial configs type. Ensure that the DifferentialMechanism and DifferentialMotorConstants instances are both constructed with the same motor type (TalonFX/TalonFXConfiguration, TalonFXS/TalonFXSConfiguration, etc.).",
413                    true
414                );
415                return;
416            }
417
418            if (!response.isOK()) {
419                System.out.println(
420                    "Talon ID " + _diffLeaderFX.getDeviceID() + " failed config with error " + response.toString()
421                );
422            }
423        }
424
425        /* disable differential control on _diffFollowerFX */
426        {
427            StatusCode response = StatusCode.OK;
428
429            if (_diffFollowerFX instanceof TalonFX diffFollowerFX) {
430                var followerConfigs = (TalonFXConfiguration)constants.FollowerInitialConfigs;
431                if (followerConfigs == null) {
432                    followerConfigs = new TalonFXConfiguration();
433                }
434                followerConfigs.DifferentialSensors = new DifferentialSensorsConfigs();
435                followerConfigs.MotorOutput.Inverted = constants.Alignment == MotorAlignmentValue.Aligned
436                    ? leaderInvert
437                    : leaderInvert == InvertedValue.CounterClockwise_Positive
438                        ? InvertedValue.Clockwise_Positive
439                        : InvertedValue.CounterClockwise_Positive;
440
441                if (constants.FollowerUsesCommonLeaderConfigs) {
442                    /* copy over common config groups from the leader */
443                    var leaderConfigs = (TalonFXConfiguration)constants.LeaderInitialConfigs;
444                    if (leaderConfigs == null) {
445                        leaderConfigs = new TalonFXConfiguration();
446                    }
447
448                    followerConfigs.Audio = leaderConfigs.Audio;
449                    followerConfigs.CurrentLimits = leaderConfigs.CurrentLimits;
450                    followerConfigs.MotorOutput = leaderConfigs.MotorOutput.clone()
451                        .withInverted(followerConfigs.MotorOutput.Inverted);
452                    followerConfigs.Voltage = leaderConfigs.Voltage;
453                    followerConfigs.TorqueCurrent = leaderConfigs.TorqueCurrent;
454                }
455
456                for (int i = 0; i < kNumConfigAttempts; ++i) {
457                    response = diffFollowerFX.getConfigurator().apply(followerConfigs);
458                    if (response.isOK()) break;
459                }
460            } else if (_diffFollowerFX instanceof TalonFXS diffFollowerFXS) {
461                var followerConfigs = (TalonFXSConfiguration)constants.FollowerInitialConfigs;
462                if (followerConfigs == null) {
463                    followerConfigs = new TalonFXSConfiguration();
464                }
465                followerConfigs.DifferentialSensors = new DifferentialSensorsConfigs();
466                followerConfigs.MotorOutput.Inverted = constants.Alignment == MotorAlignmentValue.Aligned
467                    ? leaderInvert
468                    : leaderInvert == InvertedValue.CounterClockwise_Positive
469                        ? InvertedValue.Clockwise_Positive
470                        : InvertedValue.CounterClockwise_Positive;
471
472                if (constants.FollowerUsesCommonLeaderConfigs) {
473                    /* copy over common config groups from the leader */
474                    var leaderConfigs = (TalonFXSConfiguration)constants.LeaderInitialConfigs;
475                    if (leaderConfigs == null) {
476                        leaderConfigs = new TalonFXSConfiguration();
477                    }
478
479                    followerConfigs.Audio = leaderConfigs.Audio;
480                    followerConfigs.CurrentLimits = leaderConfigs.CurrentLimits;
481                    followerConfigs.MotorOutput = leaderConfigs.MotorOutput.clone()
482                        .withInverted(followerConfigs.MotorOutput.Inverted);
483                    followerConfigs.Voltage = leaderConfigs.Voltage;
484                }
485
486                for (int i = 0; i < kNumConfigAttempts; ++i) {
487                    response = diffFollowerFXS.getConfigurator().apply(followerConfigs);
488                    if (response.isOK()) break;
489                }
490            } else {
491                DriverStation.reportError(
492                    "[phoenix] DifferentialMechanism motor type does not match the DifferentialMotorConstants initial configs type. Ensure that the DifferentialMechanism and DifferentialMotorConstants instances are both constructed with the same motor type (TalonFX/TalonFXConfiguration, TalonFXS/TalonFXSConfiguration, etc.).",
493                    true
494                );
495                return;
496            }
497
498            if (!response.isOK()) {
499                System.out.println(
500                    "Talon ID " + _diffFollowerFX.getDeviceID() + " failed config with error " + response.toString()
501                );
502            }
503        }
504    }
505
506    private StatusCode configNeutralModeImpl(MotorT motor, NeutralModeValue neutralMode, double timeoutSeconds) {
507        StatusCode status = StatusCode.OK;
508
509        var motorOutCfg = new MotorOutputConfigs();
510        if (motor instanceof TalonFX motorFX) {
511            /* First read the configs so they're up-to-date */
512            status = motorFX.getConfigurator().refresh(motorOutCfg, timeoutSeconds);
513            if (status.isOK()) {
514                /* Then set the neutral mode config to the appropriate value */
515                motorOutCfg.NeutralMode = neutralMode;
516                status = motorFX.getConfigurator().apply(motorOutCfg, timeoutSeconds);
517            }
518        } else if (motor instanceof TalonFXS motorFXS) {
519            /* First read the configs so they're up-to-date */
520            status = motorFXS.getConfigurator().refresh(motorOutCfg, timeoutSeconds);
521            if (status.isOK()) {
522                /* Then set the neutral mode config to the appropriate value */
523                motorOutCfg.NeutralMode = neutralMode;
524                status = motorFXS.getConfigurator().apply(motorOutCfg, timeoutSeconds);
525            }
526        }
527
528        if (!status.isOK()) {
529            System.out.println(
530                "Talon ID " + motor.getDeviceID() + " failed config with error " + status.toString()
531            );
532        }
533        return status;
534    }
535
536    /**
537     * Configures the neutral mode to use for both motors in the mechanism.
538     * <p>
539     * This will wait up to 0.100 seconds (100ms) by default.
540     *
541     * @param neutralMode The state of the motor controller bridge when output is neutral or disabled
542     * @return Status code of the first failed config call, or OK if all succeeded
543     */
544    public final StatusCode configNeutralMode(NeutralModeValue neutralMode) {
545        return configNeutralMode(neutralMode, 0.100);
546    }
547
548    /**
549     * Configures the neutral mode to use for both motors in the mechanism.
550     *
551     * @param neutralMode The state of the motor controller bridge when output is neutral or disabled
552     * @param timeoutSeconds Maximum amount of time to wait when performing each configuration
553     * @return Status code of the first failed config call, or OK if all succeeded
554     */
555    public final StatusCode configNeutralMode(NeutralModeValue neutralMode, double timeoutSeconds) {
556        StatusCode retval = StatusCode.OK;
557        {
558            final var status = configNeutralModeImpl(_diffLeaderFX, neutralMode, timeoutSeconds);
559            if (retval.isOK()) retval = status;
560        }
561        {
562            final var status = configNeutralModeImpl(_diffFollowerFX, neutralMode, timeoutSeconds);
563            if (retval.isOK()) retval = status;
564        }
565        return retval;
566    }
567
568    /**
569     * Call this method periodically to automatically protect against dangerous
570     * fault conditions and keep {@link #getMechanismState()} updated.
571     */
572    public final void periodic() {
573        StatusCode retval = StatusCode.OK;
574
575        /* handle remote sensor position overflow fault */
576        if (_diffLeaderFX.getFault_RemoteSensorPosOverflow().getValue()) {
577            /* fault the mechanism until the user clears it manually */
578            _requiresUserReason = RequiresUserReasonValue.RemoteSensorPosOverflow;
579            _requiresUserAction.setRelease(true);
580
581            _disabledReason = DisabledReasonValue.RemoteSensorPosOverflow;
582            retval = StatusCode.MechanismFaulted;
583        }
584
585        /* handle missing remote sensor fault */
586        if (_diffLeaderFX.getFault_RemoteSensorDataInvalid().getValue() ||
587            _diffFollowerFX.getFault_RemoteSensorDataInvalid().getValue()
588        ) {
589            /* temporarily fault the mechanism while the fault is active */
590            _disabledReason = DisabledReasonValue.MissingRemoteSensor;
591            retval = StatusCode.MechanismFaulted;
592        }
593        /* handle missing differential Talon FX fault */
594        if (_diffLeaderFX.getFault_MissingDifferentialFX().getValue()) {
595            /* temporarily fault the mechanism while the fault is active */
596            _disabledReason = DisabledReasonValue.MissingDifferentialFX;
597            retval = StatusCode.MechanismFaulted;
598        }
599
600        /* handle if any of the devices have power cycled */
601        final boolean diffLeaderFX_hasReset = _diffLeaderFXResetChecker.getAsBoolean();
602        final boolean diffFollowerFX_hasReset = _diffFollowerFXResetChecker.getAsBoolean();
603        final boolean diffSensor_hasReset = _diffSensorResetChecker.isPresent() && _diffSensorResetChecker.get().getAsBoolean();
604        final boolean diffLeaderFX_remsens_hasReset = _diffLeaderFX.getStickyFault_RemoteSensorReset().getValue();
605        final boolean diffFollowerFX_remsens_hasReset = _diffFollowerFX.getStickyFault_RemoteSensorReset().getValue();
606
607        if (diffLeaderFX_hasReset || diffFollowerFX_hasReset || diffSensor_hasReset ||
608            diffLeaderFX_remsens_hasReset || diffFollowerFX_remsens_hasReset
609        ) {
610            /* fault the mechanism until the user clears it manually */
611            _requiresUserReason = RequiresUserReasonValue.DeviceHasReset;
612            _requiresUserAction.setRelease(true);
613
614            _disabledReason = DisabledReasonValue.DeviceHasReset;
615            retval = StatusCode.MechanismFaulted;
616        }
617
618        if (retval.isOK() && _requiresUserAction.getOpaque()) {
619            /* keep the mechanism faulted until user clears the fault */
620            retval = StatusCode.MechanismFaulted;
621        }
622
623        if (!retval.isOK()) {
624            /* disable the mechanism */
625            _mechanismDisabled.setRelease(true);
626        } else {
627            /* re-enable the mechanism */
628            _disabledReason = DisabledReasonValue.None;
629            _mechanismDisabled.setRelease(false);
630        }
631    }
632
633    /**
634     * Get whether the mechanism is currently disabled due to an issue.
635     *
636     * @return true if the mechanism is temporarily disabled
637     */
638    public final boolean isDisabled() {
639        return _mechanismDisabled.getAcquire();
640    }
641
642    /**
643     * Get whether the mechanism is currently disabled and requires
644     * user action to re-enable mechanism control.
645     *
646     * @return true if the mechanism is disabled and the user must manually
647     *         perform an action
648     */
649    public final boolean requiresUserAction() {
650        return _requiresUserAction.getAcquire();
651    }
652
653    /**
654     * Gets the state of the mechanism.
655     *
656     * @return MechanismState representing the state of the mechanism
657     */
658    public final MechanismState getMechanismState() {
659        if (requiresUserAction()) {
660            return MechanismState.RequiresUserAction;
661        } else if (isDisabled()) {
662            return MechanismState.Disabled;
663        } else {
664            return MechanismState.OK;
665        }
666    }
667
668    /**
669     * Indicate to the mechanism that the user has performed the required
670     * action to resume mechanism control.
671     */
672    public final void clearUserRequirement() {
673        if (_diffLeaderFX.getStickyFault_RemoteSensorReset().getValue()) {
674            _diffLeaderFX.clearStickyFault_RemoteSensorReset();
675        }
676        if (_diffFollowerFX.getStickyFault_RemoteSensorReset().getValue()) {
677            _diffFollowerFX.clearStickyFault_RemoteSensorReset();
678        }
679        _requiresUserReason = RequiresUserReasonValue.None;
680        _requiresUserAction.setRelease(false);
681    }
682
683    /**
684     * @return The reason for the mechanism being disabled
685     */
686    public final DisabledReasonValue getDisabledReason() {
687        return _disabledReason;
688    }
689    /**
690     * @return The reason for the mechanism requiring user
691     *         action to resume control
692     */
693    public final RequiresUserReasonValue getRequiresUserReason() {
694        return _requiresUserReason;
695    }
696
697    /**
698     * Average component of the mechanism position.
699     * 
700     * <ul>
701     *   <li> <b>Minimum Value:</b> -16384.0
702     *   <li> <b>Maximum Value:</b> 16383.999755859375
703     *   <li> <b>Default Value:</b> 0
704     *   <li> <b>Units:</b> rotations
705     * </ul>
706     * <p>
707     * This refreshes and returns a cached StatusSignal object.
708     * 
709     * @return DifferentialAveragePosition Status Signal Object
710     */
711    public final StatusSignal<Angle> getAveragePosition() {
712        return getAveragePosition(true);
713    }
714    /**
715     * Average component of the mechanism position.
716     * 
717     * <ul>
718     *   <li> <b>Minimum Value:</b> -16384.0
719     *   <li> <b>Maximum Value:</b> 16383.999755859375
720     *   <li> <b>Default Value:</b> 0
721     *   <li> <b>Units:</b> rotations
722     * </ul>
723     * <p>
724     * This refreshes and returns a cached StatusSignal object.
725     * 
726     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
727     * @return DifferentialAveragePosition Status Signal Object
728     */
729    public final StatusSignal<Angle> getAveragePosition(boolean refresh) {
730        return _diffLeaderFX.getDifferentialAveragePosition(refresh);
731    }
732
733    /**
734     * Average component of the mechanism velocity.
735     * 
736     * <ul>
737     *   <li> <b>Minimum Value:</b> -512.0
738     *   <li> <b>Maximum Value:</b> 511.998046875
739     *   <li> <b>Default Value:</b> 0
740     *   <li> <b>Units:</b> rotations per second
741     * </ul>
742     * <p>
743     * This refreshes and returns a cached StatusSignal object.
744     * 
745     * @return DifferentialAverageVelocity Status Signal Object
746     */
747    public final StatusSignal<AngularVelocity> getAverageVelocity() {
748        return getAverageVelocity(true);
749    }
750    /**
751     * Average component of the mechanism velocity.
752     * 
753     * <ul>
754     *   <li> <b>Minimum Value:</b> -512.0
755     *   <li> <b>Maximum Value:</b> 511.998046875
756     *   <li> <b>Default Value:</b> 0
757     *   <li> <b>Units:</b> rotations per second
758     * </ul>
759     * <p>
760     * This refreshes and returns a cached StatusSignal object.
761     * 
762     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
763     * @return DifferentialAverageVelocity Status Signal Object
764     */
765    public final StatusSignal<AngularVelocity> getAverageVelocity(boolean refresh) {
766        return _diffLeaderFX.getDifferentialAverageVelocity(refresh);
767    }
768
769    /**
770     * Differential component of the mechanism position.
771     * 
772     * <ul>
773     *   <li> <b>Minimum Value:</b> -16384.0
774     *   <li> <b>Maximum Value:</b> 16383.999755859375
775     *   <li> <b>Default Value:</b> 0
776     *   <li> <b>Units:</b> rotations
777     * </ul>
778     * <p>
779     * This refreshes and returns a cached StatusSignal object.
780     * 
781     * @return DifferentialDifferencePosition Status Signal Object
782     */
783    public final StatusSignal<Angle> getDifferentialPosition() {
784        return getDifferentialPosition(true);
785    }
786    /**
787     * Differential component of the mechanism position.
788     * 
789     * <ul>
790     *   <li> <b>Minimum Value:</b> -16384.0
791     *   <li> <b>Maximum Value:</b> 16383.999755859375
792     *   <li> <b>Default Value:</b> 0
793     *   <li> <b>Units:</b> rotations
794     * </ul>
795     * <p>
796     * This refreshes and returns a cached StatusSignal object.
797     * 
798     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
799     * @return DifferentialDifferencePosition Status Signal Object
800     */
801    public final StatusSignal<Angle> getDifferentialPosition(boolean refresh) {
802        return _diffLeaderFX.getDifferentialDifferencePosition(refresh);
803    }
804
805    /**
806     * Differential component of the mechanism velocity.
807     * 
808     * <ul>
809     *   <li> <b>Minimum Value:</b> -512.0
810     *   <li> <b>Maximum Value:</b> 511.998046875
811     *   <li> <b>Default Value:</b> 0
812     *   <li> <b>Units:</b> rotations per second
813     * </ul>
814     * <p>
815     * This refreshes and returns a cached StatusSignal object.
816     * 
817     * @return DifferentialDifferenceVelocity Status Signal Object
818     */
819    public final StatusSignal<AngularVelocity> getDifferentialVelocity() {
820        return getDifferentialVelocity(true);
821    }
822    /**
823     * Differential component of the mechanism velocity.
824     * 
825     * <ul>
826     *   <li> <b>Minimum Value:</b> -512.0
827     *   <li> <b>Maximum Value:</b> 511.998046875
828     *   <li> <b>Default Value:</b> 0
829     *   <li> <b>Units:</b> rotations per second
830     * </ul>
831     * <p>
832     * This refreshes and returns a cached StatusSignal object.
833     * 
834     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
835     * @return DifferentialDifferenceVelocity Status Signal Object
836     */
837    public final StatusSignal<AngularVelocity> getDifferentialVelocity(boolean refresh) {
838        return _diffLeaderFX.getDifferentialDifferenceVelocity(refresh);
839    }
840
841    /**
842     * Value that the average closed loop is targeting.
843     * <p>
844     * This is the value that the closed loop PID controller targets.
845     * <p>
846     * This refreshes and returns a cached StatusSignal object.
847     * 
848     * @return ClosedLoopReference Status Signal object
849     */
850    public final StatusSignal<Double> getAverageClosedLoopReference() {
851        return getAverageClosedLoopReference(true);
852    }
853    /**
854     * Value that the average closed loop is targeting.
855     * <p>
856     * This is the value that the closed loop PID controller targets.
857     * <p>
858     * This refreshes and returns a cached StatusSignal object.
859     * 
860     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
861     * @return ClosedLoopReference Status Signal object
862     */
863    public final StatusSignal<Double> getAverageClosedLoopReference(boolean refresh) {
864        return _diffLeaderFX.getClosedLoopReference(refresh);
865    }
866
867    /**
868     * Derivative of the target that the average closed loop is targeting.
869     * <p>
870     * This is the change in the closed loop reference. This may be used
871     * in the feed-forward calculation, the derivative-error, or in
872     * application of the signage for kS. Typically, this represents the
873     * target velocity during Motion Magic®.
874     * <p>
875     * This refreshes and returns a cached StatusSignal object.
876     * 
877     * @return ClosedLoopReferenceSlope Status Signal object
878     */
879    public final StatusSignal<Double> getAverageClosedLoopReferenceSlope() {
880        return getAverageClosedLoopReferenceSlope(true);
881    }
882    /**
883     * Derivative of the target that the average closed loop is targeting.
884     * <p>
885     * This is the change in the closed loop reference. This may be used
886     * in the feed-forward calculation, the derivative-error, or in
887     * application of the signage for kS. Typically, this represents the
888     * target velocity during Motion Magic®.
889     * <p>
890     * This refreshes and returns a cached StatusSignal object.
891     * 
892     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
893     * @return ClosedLoopReferenceSlope Status Signal object
894     */
895    public final StatusSignal<Double> getAverageClosedLoopReferenceSlope(boolean refresh) {
896        return _diffLeaderFX.getClosedLoopReferenceSlope(refresh);
897    }
898
899    /**
900     * The difference between target average reference and current measurement.
901     * <p>
902     * This is the value that is treated as the error in the PID loop.
903     * <p>
904     * This refreshes and returns a cached StatusSignal object.
905     * 
906     * @return ClosedLoopError Status Signal object
907     */
908    public final StatusSignal<Double> getAverageClosedLoopError() {
909        return getAverageClosedLoopError(true);
910    }
911    /**
912     * The difference between target average reference and current measurement.
913     * <p>
914     * This is the value that is treated as the error in the PID loop.
915     * <p>
916     * This refreshes and returns a cached StatusSignal object.
917     * 
918     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
919     * @return ClosedLoopError Status Signal object
920     */
921    public final StatusSignal<Double> getAverageClosedLoopError(boolean refresh) {
922        return _diffLeaderFX.getClosedLoopError(refresh);
923    }
924
925    /**
926     * Value that the differential closed loop is targeting.
927     * <p>
928     * This is the value that the differential closed loop PID controller
929     * targets (on the difference axis).
930     * <p>
931     * This refreshes and returns a cached StatusSignal object.
932     * 
933     * @return DifferentialClosedLoopReference Status Signal object
934     */
935    public final StatusSignal<Double> getDifferentialClosedLoopReference() {
936        return getDifferentialClosedLoopReference(true);
937    }
938    /**
939     * Value that the differential closed loop is targeting.
940     * <p>
941     * This is the value that the differential closed loop PID controller
942     * targets (on the difference axis).
943     * <p>
944     * This refreshes and returns a cached StatusSignal object.
945     * 
946     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
947     * @return DifferentialClosedLoopReference Status Signal object
948     */
949    public final StatusSignal<Double> getDifferentialClosedLoopReference(boolean refresh) {
950        return _diffLeaderFX.getDifferentialClosedLoopReference(refresh);
951    }
952
953    /**
954     * Derivative of the target that the differential closed loop is
955     * targeting.
956     * <p>
957     * This is the change in the closed loop reference (on the difference
958     * axis). This may be used in the feed-forward calculation, the
959     * derivative-error, or in application of the signage for kS.
960     * Typically, this represents the target velocity during Motion
961     * Magic®.
962     * <p>
963     * This refreshes and returns a cached StatusSignal object.
964     * 
965     * @return DifferentialClosedLoopReferenceSlope Status Signal object
966     */
967    public final StatusSignal<Double> getDifferentialClosedLoopReferenceSlope() {
968        return getDifferentialClosedLoopReferenceSlope(true);
969    }
970    /**
971     * Derivative of the target that the differential closed loop is
972     * targeting.
973     * <p>
974     * This is the change in the closed loop reference (on the difference
975     * axis). This may be used in the feed-forward calculation, the
976     * derivative-error, or in application of the signage for kS.
977     * Typically, this represents the target velocity during Motion
978     * Magic®.
979     * <p>
980     * This refreshes and returns a cached StatusSignal object.
981     * 
982     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
983     * @return DifferentialClosedLoopReferenceSlope Status Signal object
984     */
985    public final StatusSignal<Double> getDifferentialClosedLoopReferenceSlope(boolean refresh) {
986        return _diffLeaderFX.getDifferentialClosedLoopReferenceSlope(refresh);
987    }
988
989    /**
990     * The difference between target differential reference and current
991     * measurement.
992     * <p>
993     * This is the value that is treated as the error in the differential
994     * PID loop (on the difference axis).
995     * <p>
996     * This refreshes and returns a cached StatusSignal object.
997     * 
998     * @return DifferentialClosedLoopError Status Signal object
999     */
1000    public final StatusSignal<Double> getDifferentialClosedLoopError() {
1001        return getDifferentialClosedLoopError(true);
1002    }
1003    /**
1004     * The difference between target differential reference and current
1005     * measurement.
1006     * <p>
1007     * This is the value that is treated as the error in the differential
1008     * PID loop (on the difference axis).
1009     * <p>
1010     * This refreshes and returns a cached StatusSignal object.
1011     * 
1012     * @param refresh Whether to refresh the StatusSignal before returning it; defaults to true
1013     * @return DifferentialClosedLoopError Status Signal object
1014     */
1015    public final StatusSignal<Double> getDifferentialClosedLoopError(boolean refresh) {
1016        return _diffLeaderFX.getDifferentialClosedLoopError(refresh);
1017    }
1018
1019    /**
1020     * Sets the position of the mechanism in rotations, assuming a differential
1021     * position of 0 rotations.
1022     * <p>
1023     * This will wait up to 0.100 seconds (100ms) by default.
1024     *
1025     * @param avgPosition The average position of the mechanism, in rotations
1026     * @return StatusCode of the set command
1027     */
1028    public final StatusCode setPosition(double avgPosition) {
1029        return setPosition(avgPosition, 0.0, 0.100);
1030    }
1031    /**
1032     * Sets the position of the mechanism in rotations.
1033     * <p>
1034     * This will wait up to 0.100 seconds (100ms) by default.
1035     *
1036     * @param avgPosition The average position of the mechanism, in rotations
1037     * @param diffPosition The differential position of the mechanism, in rotations
1038     * @return StatusCode of the set command
1039     */
1040    public final StatusCode setPosition(double avgPosition, double diffPosition) {
1041        return setPosition(avgPosition, diffPosition, 0.100);
1042    }
1043    /**
1044     * Sets the position of the mechanism in rotations.
1045     *
1046     * @param avgPosition The average position of the mechanism, in rotations
1047     * @param diffPosition The differential position of the mechanism, in rotations
1048     * @param timeoutSeconds Maximum time to wait up to in seconds
1049     * @return StatusCode of the set command
1050     */
1051    public final StatusCode setPosition(double avgPosition, double diffPosition, double timeoutSeconds) {
1052        var retval = StatusCode.OK;
1053
1054        var response = _diffLeaderFX.setPosition(avgPosition + diffPosition * _sensorToDiffRatio, timeoutSeconds);
1055        if (retval.isOK()) retval = response;
1056        response = _diffFollowerFX.setPosition(avgPosition - diffPosition * _sensorToDiffRatio, timeoutSeconds);
1057        if (retval.isOK()) retval = response;
1058
1059        return retval;
1060    }
1061
1062    /**
1063     * Sets the position of the mechanism, assuming a differential
1064     * position of 0 rotations.
1065     * <p>
1066     * This will wait up to 0.100 seconds (100ms) by default.
1067     *
1068     * @param avgPosition The average position of the mechanism
1069     * @return StatusCode of the set command
1070     */
1071    public final StatusCode setPosition(Angle avgPosition) {
1072        return setPosition(avgPosition.in(Rotations));
1073    }
1074    /**
1075     * Sets the position of the mechanism.
1076     * <p>
1077     * This will wait up to 0.100 seconds (100ms) by default.
1078     *
1079     * @param avgPosition The average position of the mechanism
1080     * @param diffPosition The differential position of the mechanism
1081     * @return StatusCode of the set command
1082     */
1083    public final StatusCode setPosition(Angle avgPosition, Angle diffPosition) {
1084        return setPosition(avgPosition.in(Rotations), diffPosition.in(Rotations));
1085    }
1086    /**
1087     * Sets the position of the mechanism.
1088     *
1089     * @param avgPosition The average position of the mechanism
1090     * @param diffPosition The differential position of the mechanism
1091     * @param timeoutSeconds Maximum time to wait up to in seconds
1092     * @return StatusCode of the set command
1093     */
1094    public final StatusCode setPosition(Angle avgPosition, Angle diffPosition, double timeoutSeconds) {
1095        return setPosition(avgPosition.in(Rotations), diffPosition.in(Rotations), timeoutSeconds);
1096    }
1097
1098    /**
1099     * Get the Talon FX that is differential leader. The differential
1100     * leader calculates the output for the differential follower. The
1101     * differential leader is also useful for fault detection, and it
1102     * reports status signals for the differential controller.
1103     *
1104     * @return Differential leader Talon FX
1105     */
1106    public final MotorT getLeader() {
1107        return _diffLeaderFX;
1108    }
1109
1110    /**
1111     * Get the Talon FX that is differential follower. The differential
1112     * follower's position and velocity are used by the differential leader
1113     * for the differential controller.
1114     *
1115     * @return Differential follower Talon FX
1116     */
1117    public final MotorT getFollower() {
1118        return _diffFollowerFX;
1119    }
1120
1121    private StatusCode beforeControl() {
1122        StatusCode retval = StatusCode.OK;
1123        if (_mechanismDisabled.getOpaque()) {
1124            /* disable the mechanism */
1125            retval = StatusCode.MechanismFaulted;
1126        }
1127
1128        if (!retval.isOK()) {
1129            /* neutral the output */
1130            setNeutralOut();
1131        }
1132        return retval;
1133    }
1134
1135    /**
1136     * Request neutral output of mechanism. The applied brake type
1137     * is determined by the NeutralMode configuration of each device.
1138     * <p>
1139     * Since the NeutralMode configuration of devices may not align, users
1140     * may prefer to use the {@link #setCoastOut()} or {@link #setStaticBrake()} method.
1141     *
1142     * @return Status Code of the request.
1143     */
1144    public final StatusCode setNeutralOut() {
1145        var retval = StatusCode.OK;
1146        {
1147            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_neutral);
1148            if (retval.isOK()) {
1149                retval = _diffLeaderFX_retval;
1150            }
1151        }
1152        {
1153            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_neutral);
1154            if (retval.isOK()) {
1155                retval = _diffFollowerFX_retval;
1156            }
1157        }
1158        return retval;
1159    }
1160
1161    /**
1162     * Request coast neutral output of mechanism. The bridge is
1163     * disabled and the rotor is allowed to coast.
1164     *
1165     * @return Status Code of the request.
1166     */
1167    public final StatusCode setCoastOut() {
1168        var retval = StatusCode.OK;
1169        {
1170            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_coast);
1171            if (retval.isOK()) {
1172                retval = _diffLeaderFX_retval;
1173            }
1174        }
1175        {
1176            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_coast);
1177            if (retval.isOK()) {
1178                retval = _diffFollowerFX_retval;
1179            }
1180        }
1181        return retval;
1182    }
1183
1184    /**
1185     * Applies full neutral-brake on the mechanism by shorting
1186     * motor leads together.
1187     *
1188     * @return Status Code of the request.
1189     */
1190    public final StatusCode setStaticBrake() {
1191        var retval = StatusCode.OK;
1192        {
1193            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_brake);
1194            if (retval.isOK()) {
1195                retval = _diffLeaderFX_retval;
1196            }
1197        }
1198        {
1199            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_brake);
1200            if (retval.isOK()) {
1201                retval = _diffFollowerFX_retval;
1202            }
1203        }
1204        return retval;
1205    }
1206
1207    
1208    
1209    /**
1210     * Sets the control request for this mechanism.
1211     *
1212     * @param diffLeaderFXRequest Request a specified motor duty cycle with a
1213     *                            differential position closed-loop.
1214     * @return Status Code of the request.
1215     */
1216    public final StatusCode setControl(DifferentialDutyCycle diffLeaderFXRequest) {
1217        var retval = StatusCode.OK;
1218    
1219        retval = beforeControl();
1220        if (!retval.isOK()) {
1221            return retval;
1222        }
1223        
1224        {
1225            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1226            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1227            if (retval.isOK()) {
1228                retval = _diffLeaderFX_retval;
1229            }
1230        }
1231        
1232        {
1233            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1234            if (retval.isOK()) {
1235                retval = _diffFollowerFX_retval;
1236            }
1237        }
1238        
1239        return retval;
1240    }
1241    
1242    /**
1243     * Sets the control request for this mechanism.
1244     *
1245     * @param diffLeaderFXRequest Request a specified voltage with a differential
1246     *                            position closed-loop.
1247     * @return Status Code of the request.
1248     */
1249    public final StatusCode setControl(DifferentialVoltage diffLeaderFXRequest) {
1250        var retval = StatusCode.OK;
1251    
1252        retval = beforeControl();
1253        if (!retval.isOK()) {
1254            return retval;
1255        }
1256        
1257        {
1258            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1259            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1260            if (retval.isOK()) {
1261                retval = _diffLeaderFX_retval;
1262            }
1263        }
1264        
1265        {
1266            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1267            if (retval.isOK()) {
1268                retval = _diffFollowerFX_retval;
1269            }
1270        }
1271        
1272        return retval;
1273    }
1274    
1275    /**
1276     * Sets the control request for this mechanism.
1277     *
1278     * @param diffLeaderFXRequest Request PID to target position with a differential
1279     *                            position setpoint.
1280     * @return Status Code of the request.
1281     */
1282    public final StatusCode setControl(DifferentialPositionDutyCycle diffLeaderFXRequest) {
1283        var retval = StatusCode.OK;
1284    
1285        retval = beforeControl();
1286        if (!retval.isOK()) {
1287            return retval;
1288        }
1289        
1290        {
1291            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1292            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1293            if (retval.isOK()) {
1294                retval = _diffLeaderFX_retval;
1295            }
1296        }
1297        
1298        {
1299            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1300            if (retval.isOK()) {
1301                retval = _diffFollowerFX_retval;
1302            }
1303        }
1304        
1305        return retval;
1306    }
1307    
1308    /**
1309     * Sets the control request for this mechanism.
1310     *
1311     * @param diffLeaderFXRequest Request PID to target position with a differential
1312     *                            position setpoint
1313     * @return Status Code of the request.
1314     */
1315    public final StatusCode setControl(DifferentialPositionVoltage diffLeaderFXRequest) {
1316        var retval = StatusCode.OK;
1317    
1318        retval = beforeControl();
1319        if (!retval.isOK()) {
1320            return retval;
1321        }
1322        
1323        {
1324            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1325            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1326            if (retval.isOK()) {
1327                retval = _diffLeaderFX_retval;
1328            }
1329        }
1330        
1331        {
1332            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1333            if (retval.isOK()) {
1334                retval = _diffFollowerFX_retval;
1335            }
1336        }
1337        
1338        return retval;
1339    }
1340    
1341    /**
1342     * Sets the control request for this mechanism.
1343     *
1344     * @param diffLeaderFXRequest Request PID to target velocity with a differential
1345     *                            position setpoint.
1346     * @return Status Code of the request.
1347     */
1348    public final StatusCode setControl(DifferentialVelocityDutyCycle diffLeaderFXRequest) {
1349        var retval = StatusCode.OK;
1350    
1351        retval = beforeControl();
1352        if (!retval.isOK()) {
1353            return retval;
1354        }
1355        
1356        {
1357            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1358            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1359            if (retval.isOK()) {
1360                retval = _diffLeaderFX_retval;
1361            }
1362        }
1363        
1364        {
1365            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1366            if (retval.isOK()) {
1367                retval = _diffFollowerFX_retval;
1368            }
1369        }
1370        
1371        return retval;
1372    }
1373    
1374    /**
1375     * Sets the control request for this mechanism.
1376     *
1377     * @param diffLeaderFXRequest Request PID to target velocity with a differential
1378     *                            position setpoint.
1379     * @return Status Code of the request.
1380     */
1381    public final StatusCode setControl(DifferentialVelocityVoltage diffLeaderFXRequest) {
1382        var retval = StatusCode.OK;
1383    
1384        retval = beforeControl();
1385        if (!retval.isOK()) {
1386            return retval;
1387        }
1388        
1389        {
1390            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1391            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1392            if (retval.isOK()) {
1393                retval = _diffLeaderFX_retval;
1394            }
1395        }
1396        
1397        {
1398            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1399            if (retval.isOK()) {
1400                retval = _diffFollowerFX_retval;
1401            }
1402        }
1403        
1404        return retval;
1405    }
1406    
1407    /**
1408     * Sets the control request for this mechanism.
1409     *
1410     * @param diffLeaderFXRequest Requests Motion Magic® to target a final position
1411     *                            using a motion profile, and PID to a differential
1412     *                            position setpoint.
1413     * @return Status Code of the request.
1414     */
1415    public final StatusCode setControl(DifferentialMotionMagicDutyCycle diffLeaderFXRequest) {
1416        var retval = StatusCode.OK;
1417    
1418        retval = beforeControl();
1419        if (!retval.isOK()) {
1420            return retval;
1421        }
1422        
1423        {
1424            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1425            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1426            if (retval.isOK()) {
1427                retval = _diffLeaderFX_retval;
1428            }
1429        }
1430        
1431        {
1432            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1433            if (retval.isOK()) {
1434                retval = _diffFollowerFX_retval;
1435            }
1436        }
1437        
1438        return retval;
1439    }
1440    
1441    /**
1442     * Sets the control request for this mechanism.
1443     *
1444     * @param diffLeaderFXRequest Requests Motion Magic® to target a final position
1445     *                            using a motion profile, and PID to a differential
1446     *                            position setpoint.
1447     * @return Status Code of the request.
1448     */
1449    public final StatusCode setControl(DifferentialMotionMagicVoltage diffLeaderFXRequest) {
1450        var retval = StatusCode.OK;
1451    
1452        retval = beforeControl();
1453        if (!retval.isOK()) {
1454            return retval;
1455        }
1456        
1457        {
1458            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1459            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1460            if (retval.isOK()) {
1461                retval = _diffLeaderFX_retval;
1462            }
1463        }
1464        
1465        {
1466            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1467            if (retval.isOK()) {
1468                retval = _diffFollowerFX_retval;
1469            }
1470        }
1471        
1472        return retval;
1473    }
1474    
1475    /**
1476     * Sets the control request for this mechanism.
1477     *
1478     * @param diffLeaderFXRequest Requests Motion Magic® to target a final position
1479     *                            using an exponential motion profile, and PID to a
1480     *                            differential position setpoint.
1481     * @return Status Code of the request.
1482     */
1483    public final StatusCode setControl(DifferentialMotionMagicExpoDutyCycle diffLeaderFXRequest) {
1484        var retval = StatusCode.OK;
1485    
1486        retval = beforeControl();
1487        if (!retval.isOK()) {
1488            return retval;
1489        }
1490        
1491        {
1492            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1493            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1494            if (retval.isOK()) {
1495                retval = _diffLeaderFX_retval;
1496            }
1497        }
1498        
1499        {
1500            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1501            if (retval.isOK()) {
1502                retval = _diffFollowerFX_retval;
1503            }
1504        }
1505        
1506        return retval;
1507    }
1508    
1509    /**
1510     * Sets the control request for this mechanism.
1511     *
1512     * @param diffLeaderFXRequest Requests Motion Magic® to target a final position
1513     *                            using an exponential motion profile, and PID to a
1514     *                            differential position setpoint.
1515     * @return Status Code of the request.
1516     */
1517    public final StatusCode setControl(DifferentialMotionMagicExpoVoltage diffLeaderFXRequest) {
1518        var retval = StatusCode.OK;
1519    
1520        retval = beforeControl();
1521        if (!retval.isOK()) {
1522            return retval;
1523        }
1524        
1525        {
1526            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1527            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1528            if (retval.isOK()) {
1529                retval = _diffLeaderFX_retval;
1530            }
1531        }
1532        
1533        {
1534            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1535            if (retval.isOK()) {
1536                retval = _diffFollowerFX_retval;
1537            }
1538        }
1539        
1540        return retval;
1541    }
1542    
1543    /**
1544     * Sets the control request for this mechanism.
1545     *
1546     * @param diffLeaderFXRequest Requests Motion Magic® to target a final velocity
1547     *                            using a motion profile, and PID to a differential
1548     *                            position setpoint.  This allows smooth transitions
1549     *                            between velocity set points.
1550     * @return Status Code of the request.
1551     */
1552    public final StatusCode setControl(DifferentialMotionMagicVelocityDutyCycle diffLeaderFXRequest) {
1553        var retval = StatusCode.OK;
1554    
1555        retval = beforeControl();
1556        if (!retval.isOK()) {
1557            return retval;
1558        }
1559        
1560        {
1561            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1562            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1563            if (retval.isOK()) {
1564                retval = _diffLeaderFX_retval;
1565            }
1566        }
1567        
1568        {
1569            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1570            if (retval.isOK()) {
1571                retval = _diffFollowerFX_retval;
1572            }
1573        }
1574        
1575        return retval;
1576    }
1577    
1578    /**
1579     * Sets the control request for this mechanism.
1580     *
1581     * @param diffLeaderFXRequest Requests Motion Magic® to target a final velocity
1582     *                            using a motion profile, and PID to a differential
1583     *                            position setpoint.  This allows smooth transitions
1584     *                            between velocity set points.
1585     * @return Status Code of the request.
1586     */
1587    public final StatusCode setControl(DifferentialMotionMagicVelocityVoltage diffLeaderFXRequest) {
1588        var retval = StatusCode.OK;
1589    
1590        retval = beforeControl();
1591        if (!retval.isOK()) {
1592            return retval;
1593        }
1594        
1595        {
1596            final var _diffLeaderFX_reqPtr = diffLeaderFXRequest;
1597            final var _diffLeaderFX_retval = _diffLeaderFX.setControl(_diffLeaderFX_reqPtr);
1598            if (retval.isOK()) {
1599                retval = _diffLeaderFX_retval;
1600            }
1601        }
1602        
1603        {
1604            final var _diffFollowerFX_retval = _diffFollowerFX.setControl(_diffFollow);
1605            if (retval.isOK()) {
1606                retval = _diffFollowerFX_retval;
1607            }
1608        }
1609        
1610        return retval;
1611    }
1612    
1613}