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.concurrent.atomic.AtomicBoolean;
011import java.util.function.BooleanSupplier;
012
013import com.ctre.phoenix6.StatusCode;
014import com.ctre.phoenix6.configs.DifferentialSensorsConfigs;
015import com.ctre.phoenix6.controls.DifferentialFollower;
016import com.ctre.phoenix6.controls.NeutralOut;
017import com.ctre.phoenix6.controls.CoastOut;
018import com.ctre.phoenix6.controls.StaticBrake;
019import com.ctre.phoenix6.hardware.CANcoder;
020import com.ctre.phoenix6.hardware.Pigeon2;
021import com.ctre.phoenix6.hardware.TalonFX;
022import com.ctre.phoenix6.signals.DifferentialSensorSourceValue;
023
024import com.ctre.phoenix6.controls.DifferentialDutyCycle;
025import com.ctre.phoenix6.controls.DifferentialMotionMagicDutyCycle;
026import com.ctre.phoenix6.controls.DifferentialMotionMagicVoltage;
027import com.ctre.phoenix6.controls.DifferentialPositionDutyCycle;
028import com.ctre.phoenix6.controls.DifferentialPositionVoltage;
029import com.ctre.phoenix6.controls.DifferentialVelocityDutyCycle;
030import com.ctre.phoenix6.controls.DifferentialVelocityVoltage;
031import com.ctre.phoenix6.controls.DifferentialVoltage;
032
033/**
034 * Manages control of a simple two-axis differential mechanism.
035 *
036 * This mechanism provides limited differential functionality. Pro users
037 * on a CAN FD bus can use the {@link DifferentialMechanism} class instead
038 * for full functionality.
039 */
040public class SimpleDifferentialMechanism {
041    /**
042     * Sensor sources for a differential Pigeon 2.
043     */
044    public enum DifferentialPigeon2Source {
045        Yaw,
046        Pitch,
047        Roll
048    }
049
050    /**
051     * Possible reasons for the mechanism to disable.
052     */
053    public enum DisabledReason {
054        /**
055         * No reason given.
056         */
057        None,
058        /**
059         * A remote sensor is not present on CAN Bus.
060         */
061        MissingRemoteSensor,
062        /**
063         * The remote Talon FX used for differential
064         * control is not present on CAN Bus.
065         */
066        MissingDifferentialFX,
067        /**
068         * A remote sensor position has overflowed. Because of the nature
069         * of remote sensors, it is possible for a remote sensor position
070         * to overflow beyond what is supported by the status signal frame.
071         * However, this is rare and cannot occur over the course of an FRC
072         * match under normal use.
073         */
074        RemoteSensorPosOverflow,
075        /**
076         * A device or remote sensor has reset.
077         */
078        DeviceHasReset,
079    }
080
081    /**
082     * Possible reasons for the mechanism to require
083     * user action to resume control.
084     */
085    public enum RequiresUserReason {
086        /**
087         * No reason given.
088         */
089        None,
090        /**
091         * A remote sensor position has overflowed. Because of the nature
092         * of remote sensors, it is possible for a remote sensor position
093         * to overflow beyond what is supported by the status signal frame.
094         * However, this is rare and cannot occur over the course of an FRC
095         * match under normal use.
096         */
097        RemoteSensorPosOverflow,
098        /**
099         * A device or remote sensor has reset.
100         */
101        DeviceHasReset,
102    }
103
104    /**
105     * The default number of retries for config applies.
106     */
107    public static final int kDefaultConfigRetries = 5;
108
109    private final TalonFX _diffAddFX;
110    private final TalonFX _diffSubFX;
111    private final Optional<Pigeon2> _pigeon2;
112    private final DifferentialPigeon2Source _pigeonSource;
113    private final Optional<CANcoder> _cancoder;
114
115    private final DifferentialFollower _diffFollow;
116
117    private final NeutralOut _neutral = new NeutralOut();
118    private final CoastOut _coast = new CoastOut();
119    private final StaticBrake _brake = new StaticBrake();
120
121    private final BooleanSupplier _diffAddFXResetChecker;
122    private final BooleanSupplier _diffSubFXResetChecker;
123    private final Optional<BooleanSupplier> _pigeon2ResetChecker;
124    private final Optional<BooleanSupplier> _cancoderResetChecker;
125
126    private boolean _hasAppliedConfigs = false;
127
128    private final AtomicBoolean _mechanismDisabled = new AtomicBoolean(false);
129    private final AtomicBoolean _requiresUserAction = new AtomicBoolean(false);
130
131    private DisabledReason _disabledReason = DisabledReason.None;
132    private RequiresUserReason _requiresUserReason = RequiresUserReason.None;
133
134    /**
135     * Creates a new simple differential mechanism using the given two {@link TalonFX} devices.
136     * The mechanism will use the average of the two Talon FX sensors on the primary axis,
137     * and the difference between the two Talon FX sensors on the differential axis.
138     * <p>
139     * This mechanism provides limited differential functionality. Pro users on a CAN FD
140     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
141     *
142     * @param differentialAddFX The Talon FX that will have the differential output added to its regular output.
143     * @param differentialSubFX The Talon FX that will have the differential output subtracted from its regular output.
144     * @param motorDirectionsAlign Whether the differential motors' directions are aligned.
145     */
146    public SimpleDifferentialMechanism(TalonFX differentialAddFX, TalonFX differentialSubFX, boolean motorDirectionsAlign)
147    {
148        _diffAddFX = differentialAddFX;
149        _diffSubFX = differentialSubFX;
150        _pigeon2 = Optional.empty();
151        _pigeonSource = null;
152        _cancoder = Optional.empty();
153        _diffFollow = new DifferentialFollower(_diffAddFX.getDeviceID(), !motorDirectionsAlign);
154        _diffAddFXResetChecker = _diffAddFX.getResetOccurredChecker();
155        _diffSubFXResetChecker = _diffSubFX.getResetOccurredChecker();
156        _pigeon2ResetChecker = Optional.empty();
157        _cancoderResetChecker = Optional.empty();
158    }
159
160    /**
161     * Creates a new simple differential mechanism using the given two {@link TalonFX} devices and
162     * a {@link Pigeon2}. The mechanism will use the average of the two Talon FX sensors on the
163     * primary axis, and the selected Pigeon 2 sensor source on the differential axis.
164     * <p>
165     * This mechanism provides limited differential functionality. Pro users on a CAN FD
166     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
167     *
168     * @param differentialAddFX The Talon FX that will have the differential output added to its regular output.
169     * @param differentialSubFX The Talon FX that will have the differential output subtracted from its regular output.
170     * @param motorDirectionsAlign Whether the differential motors' directions are aligned.
171     * @param pigeon2 The Pigeon 2 to use for the differential axis.
172     * @param pigeonSource The sensor source to use for the Pigeon 2 (Yaw, Pitch, or Roll).
173     */
174    public SimpleDifferentialMechanism(TalonFX differentialAddFX, TalonFX differentialSubFX, boolean motorDirectionsAlign, Pigeon2 pigeon2, DifferentialPigeon2Source pigeonSource)
175    {
176        _diffAddFX = differentialAddFX;
177        _diffSubFX = differentialSubFX;
178        _pigeon2 = Optional.of(pigeon2);
179        _pigeonSource = pigeonSource;
180        _cancoder = Optional.empty();
181        _diffFollow = new DifferentialFollower(_diffAddFX.getDeviceID(), !motorDirectionsAlign);
182        _diffAddFXResetChecker = _diffAddFX.getResetOccurredChecker();
183        _diffSubFXResetChecker = _diffSubFX.getResetOccurredChecker();
184        _pigeon2ResetChecker = Optional.of(_pigeon2.get().getResetOccurredChecker());
185        _cancoderResetChecker = Optional.empty();
186    }
187
188    /**
189     * Creates a new simple differential mechanism using the given two {@link TalonFX} devices and
190     * a {@link CANcoder}. The mechanism will use the average of the two Talon FX sensors on the
191     * primary axis, and the CANcoder position/velocity on the differential axis.
192     * <p>
193     * This mechanism provides limited differential functionality. Pro users on a CAN FD
194     * bus can use the {@link DifferentialMechanism} class instead for full functionality.
195     *
196     * @param differentialAddFX The Talon FX that will have the differential output added to its regular output.
197     * @param differentialSubFX The Talon FX that will have the differential output subtracted from its regular output.
198     * @param motorDirectionsAlign Whether the differential motors' directions are aligned.
199     * @param cancoder The CANcoder to use for the differential axis.
200     */
201    public SimpleDifferentialMechanism(TalonFX differentialAddFX, TalonFX differentialSubFX, boolean motorDirectionsAlign, CANcoder cancoder)
202    {
203        _diffAddFX = differentialAddFX;
204        _diffSubFX = differentialSubFX;
205        _pigeon2 = Optional.empty();
206        _pigeonSource = null;
207        _cancoder = Optional.of(cancoder);
208        _diffFollow = new DifferentialFollower(_diffAddFX.getDeviceID(), !motorDirectionsAlign);
209        _diffAddFXResetChecker = _diffAddFX.getResetOccurredChecker();
210        _diffSubFXResetChecker = _diffSubFX.getResetOccurredChecker();
211        _pigeon2ResetChecker = Optional.empty();
212        _cancoderResetChecker = Optional.of(_cancoder.get().getResetOccurredChecker());
213    }
214
215    /**
216     * Get the Talon FX that is differential leader. The differential
217     * leader calculates the output for the differential follower. The
218     * differential leader is also useful for fault detection, and it
219     * reports status signals for the differential controller.
220     *
221     * @return Differential leader Talon FX
222     */
223    public TalonFX getDifferentialLeader()
224    {
225        return _diffAddFX;
226    }
227
228    /**
229     * Get the Talon FX that is differential follower. The differential
230     * follower's position and velocity are used by the differential leader
231     * for the differential controller.
232     *
233     * @return Differential follower Talon FX
234     */
235    public TalonFX getDifferentialFollower()
236    {
237        return _diffSubFX;
238    }
239
240    /**
241     * Apply the mechanism configs to the devices. This should be
242     * called after applying all other configs to the devices.
243     * <p>
244     * If the user does not call this function by the time SetControl
245     * is called, SetControl will apply the configs once.
246     * <p>
247     * This function defaults to retrying up to {@link #kDefaultConfigRetries}.
248     *
249     * @return Status Code of the config applies.
250     */
251    public StatusCode applyConfigs() {
252        return applyConfigs(kDefaultConfigRetries);
253    }
254
255    /**
256     * Apply the mechanism configs to the devices. This should be
257     * called after applying all other configs to the devices.
258     * <p>
259     * If the user does not call this function by the time SetControl
260     * is called, SetControl will apply the configs once.
261     *
262     * @param numRetries Number of retries when applying the configs
263     * @return Status Code of the config applies.
264     */
265    public StatusCode applyConfigs(int numRetries)
266    {
267        StatusCode retval = StatusCode.OK;
268
269        if (numRetries < 1) numRetries = 1;
270
271        /*
272         * The onboard differential controller adds the differential output to its own output
273         * and subtracts the differential output for differential followers, so use _diffAddFX
274         * as the primary controller.
275         */
276        final var diff_cfg = new DifferentialSensorsConfigs();
277
278        /* disable differential control on _diffSubFX */
279        {
280            StatusCode _diffSubFX_retval = StatusCode.OK;
281            for (int i = 0; i < numRetries; ++i) {
282                _diffSubFX_retval = _diffSubFX.getConfigurator().apply(diff_cfg);
283                if (_diffSubFX_retval.isOK()) break;
284            }
285            if (retval.isOK()) {
286                retval = _diffSubFX_retval;
287            }
288        }
289
290        /* set up differential control on _diffAddFX */
291        diff_cfg.DifferentialTalonFXSensorID = _diffSubFX.getDeviceID();
292        if (_pigeon2.isPresent()) {
293            /* use the Pigeon 2 for differential control */
294            switch (_pigeonSource) {
295                case Yaw:
296                default:
297                    diff_cfg.DifferentialSensorSource = DifferentialSensorSourceValue.RemotePigeon2_Yaw;
298                    break;
299                case Pitch:
300                    diff_cfg.DifferentialSensorSource = DifferentialSensorSourceValue.RemotePigeon2_Pitch;
301                    break;
302                case Roll:
303                    diff_cfg.DifferentialSensorSource = DifferentialSensorSourceValue.RemotePigeon2_Roll;
304                    break;
305            }
306            diff_cfg.DifferentialRemoteSensorID = _pigeon2.get().getDeviceID();
307        } else if (_cancoder.isPresent()) {
308            /* use the CANcoder for differential control */
309            diff_cfg.DifferentialSensorSource = DifferentialSensorSourceValue.RemoteCANcoder;
310            diff_cfg.DifferentialRemoteSensorID = _cancoder.get().getDeviceID();
311        } else {
312            /* use the difference between the two Talon FXs for differential control */
313            diff_cfg.DifferentialSensorSource = DifferentialSensorSourceValue.RemoteTalonFX_Diff;
314        }
315
316        {
317            StatusCode _diffAddFX_retval = StatusCode.OK;
318            for (int i = 0; i < numRetries; ++i) {
319                _diffAddFX_retval = _diffAddFX.getConfigurator().apply(diff_cfg);
320                if (_diffAddFX_retval.isOK()) break;
321            }
322            if (retval.isOK()) {
323                retval = _diffAddFX_retval;
324            }
325        }
326
327        if (retval.isOK()) {
328            _hasAppliedConfigs = true;
329        }
330
331        return retval;
332    }
333
334    /**
335     * Call this method periodically to keep the mechanism state updated.
336     */
337    public void periodic()
338    {
339        StatusCode retval = StatusCode.OK;
340
341        /* handle remote sensor position overflow fault */
342        if (_diffAddFX.getFault_RemoteSensorPosOverflow().getValue()) {
343            /* fault the mechanism until the user clears it manually */
344            _requiresUserReason = RequiresUserReason.RemoteSensorPosOverflow;
345            _requiresUserAction.setRelease(true);
346
347            _disabledReason = DisabledReason.RemoteSensorPosOverflow;
348            retval = StatusCode.MechanismFaulted;
349        }
350
351        /* handle missing remote sensor fault */
352        if (_diffAddFX.getFault_RemoteSensorDataInvalid().getValue() ||
353            _diffSubFX.getFault_RemoteSensorDataInvalid().getValue()
354        ) {
355            /* temporarily fault the mechanism while the fault is active */
356            _disabledReason = DisabledReason.MissingRemoteSensor;
357            retval = StatusCode.MechanismFaulted;
358        }
359        /* handle missing differential Talon FX fault */
360        if (_diffAddFX.getFault_MissingDifferentialFX().getValue()) {
361            /* temporarily fault the mechanism while the fault is active */
362            _disabledReason = DisabledReason.MissingDifferentialFX;
363            retval = StatusCode.MechanismFaulted;
364        }
365
366        /* handle if any of the devices have power cycled */
367        final boolean diffAddFX_hasReset = _diffAddFXResetChecker.getAsBoolean();
368        final boolean diffSubFX_hasReset = _diffSubFXResetChecker.getAsBoolean();
369        final boolean pigeon2_hasReset = _pigeon2ResetChecker.isPresent() && _pigeon2ResetChecker.get().getAsBoolean();
370        final boolean cancoder_hasReset = _cancoderResetChecker.isPresent() && _cancoderResetChecker.get().getAsBoolean();
371        final boolean diffAddFX_remsens_hasReset = _diffAddFX.getStickyFault_RemoteSensorReset().getValue();
372        final boolean diffSubFX_remsens_hasReset = _diffSubFX.getStickyFault_RemoteSensorReset().getValue();
373
374        if (diffAddFX_hasReset || diffSubFX_hasReset ||
375            pigeon2_hasReset || cancoder_hasReset ||
376            diffAddFX_remsens_hasReset || diffSubFX_remsens_hasReset
377        ) {
378            /* fault the mechanism until the user clears it manually */
379            _requiresUserReason = RequiresUserReason.DeviceHasReset;
380            _requiresUserAction.setRelease(true);
381
382            _disabledReason = DisabledReason.DeviceHasReset;
383            retval = StatusCode.MechanismFaulted;
384        }
385
386        if (retval.isOK() && _requiresUserAction.getOpaque()) {
387            /* keep the mechanism faulted until user clears the fault */
388            retval = StatusCode.MechanismFaulted;
389        }
390
391        if (!retval.isOK()) {
392            /* disable the mechanism */
393            _mechanismDisabled.setRelease(true);
394        } else {
395            /* re-enable the mechanism */
396            _disabledReason = DisabledReason.None;
397            _mechanismDisabled.setRelease(false);
398        }
399    }
400
401    /**
402     * Get whether the mechanism is currently disabled due to an issue.
403     *
404     * @return true if the mechanism is temporarily disabled
405     */
406    public boolean isDisabled()
407    {
408        return _mechanismDisabled.getAcquire();
409    }
410
411    /**
412     * Get whether the mechanism is currently disabled and requires
413     * user action to re-enable mechanism control.
414     *
415     * @return true if the mechanism is disabled and the user must manually
416     *         perform an action
417     */
418    public boolean requiresUserAction()
419    {
420        return _requiresUserAction.getAcquire();
421    }
422
423    /**
424     * Gets the state of the mechanism.
425     *
426     * @return MechanismState representing the state of the mechanism
427     */
428    public MechanismState getMechanismState()
429    {
430        if (requiresUserAction()) {
431            return MechanismState.RequiresUserAction;
432        } else if (isDisabled()) {
433            return MechanismState.Disabled;
434        } else {
435            return MechanismState.OK;
436        }
437    }
438
439    /**
440     * Indicate to the mechanism that the user has performed the required
441     * action to resume mechanism control.
442     */
443    public void clearUserRequirement()
444    {
445        if (_diffAddFX.getStickyFault_RemoteSensorReset().getValue()) {
446            _diffAddFX.clearStickyFault_RemoteSensorReset();
447        }
448        if (_diffSubFX.getStickyFault_RemoteSensorReset().getValue()) {
449            _diffSubFX.clearStickyFault_RemoteSensorReset();
450        }
451        _requiresUserReason = RequiresUserReason.None;
452        _requiresUserAction.setRelease(false);
453    }
454
455    /**
456     * @return The reason for the mechanism being disabled
457     */
458    public DisabledReason getDisabledReason()
459    {
460        return _disabledReason;
461    }
462    /**
463     * @return The reason for the mechanism requiring user
464     *         action to resume control
465     */
466    public RequiresUserReason getRequiresUserReason()
467    {
468        return _requiresUserReason;
469    }
470
471    private StatusCode beforeControl()
472    {
473        StatusCode retval = StatusCode.OK;
474        if (!_hasAppliedConfigs) {
475            retval = applyConfigs();
476        }
477
478        if (retval.isOK() && _mechanismDisabled.getOpaque()) {
479            /* disable the mechanism */
480            retval = StatusCode.MechanismFaulted;
481        }
482
483        if (!retval.isOK()) {
484            /* neutral the output */
485            setNeutralOut();
486        }
487        return retval;
488    }
489
490    /**
491     * Request neutral output of mechanism. The applied brake type
492     * is determined by the NeutralMode configuration of each device.
493     * <p>
494     * Since the NeutralMode configuration of devices may not align, users
495     * may prefer to use the {@link #setCoastOut()} or {@link #setStaticBrake()} method.
496     *
497     * @return Status Code of the request.
498     */
499    public StatusCode setNeutralOut()
500    {
501        var retval = StatusCode.OK;
502        {
503            final var _diffAddFX_retval = _diffAddFX.setControl(_neutral);
504            if (retval.isOK()) {
505                retval = _diffAddFX_retval;
506            }
507        }
508        {
509            final var _diffSubFX_retval = _diffSubFX.setControl(_neutral);
510            if (retval.isOK()) {
511                retval = _diffSubFX_retval;
512            }
513        }
514        return retval;
515    }
516
517    /**
518     * Request coast neutral output of mechanism. The bridge is
519     * disabled and the rotor is allowed to coast.
520     *
521     * @return Status Code of the request.
522     */
523    public StatusCode setCoastOut()
524    {
525        var retval = StatusCode.OK;
526        {
527            final var _diffAddFX_retval = _diffAddFX.setControl(_coast);
528            if (retval.isOK()) {
529                retval = _diffAddFX_retval;
530            }
531        }
532        {
533            final var _diffSubFX_retval = _diffSubFX.setControl(_coast);
534            if (retval.isOK()) {
535                retval = _diffSubFX_retval;
536            }
537        }
538        return retval;
539    }
540
541    /**
542     * Applies full neutral-brake on the mechanism by shorting
543     * motor leads together.
544     *
545     * @return Status Code of the request.
546     */
547    public StatusCode setStaticBrake()
548    {
549        var retval = StatusCode.OK;
550        {
551            final var _diffAddFX_retval = _diffAddFX.setControl(_brake);
552            if (retval.isOK()) {
553                retval = _diffAddFX_retval;
554            }
555        }
556        {
557            final var _diffSubFX_retval = _diffSubFX.setControl(_brake);
558            if (retval.isOK()) {
559                retval = _diffSubFX_retval;
560            }
561        }
562        return retval;
563    }
564
565    
566    
567    /**
568     * Sets the control request for this mechanism.
569     *
570     * @param _diffAddFXRequest    Request a specified motor duty cycle with a
571     *                             differential position closed-loop.
572     * @return Status Code of the request.
573     */
574    public StatusCode setControl(DifferentialDutyCycle _diffAddFXRequest)
575    {
576        var retval = StatusCode.OK;
577    
578        retval = beforeControl();
579        if (!retval.isOK()) {
580            return retval;
581        }
582        
583        {    
584            final var _diffAddFX_reqPtr = _diffAddFXRequest;
585            
586            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
587            if (retval.isOK()) {
588                retval = _diffAddFX_retval;
589            }
590        }
591        
592        {
593            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
594            if (retval.isOK()) {
595                retval = _diffSubFX_retval;
596            }
597        }
598        
599        return retval;
600    }
601    
602    /**
603     * Sets the control request for this mechanism.
604     *
605     * @param _diffAddFXRequest    Request a specified voltage with a differential
606     *                             position closed-loop.
607     * @return Status Code of the request.
608     */
609    public StatusCode setControl(DifferentialVoltage _diffAddFXRequest)
610    {
611        var retval = StatusCode.OK;
612    
613        retval = beforeControl();
614        if (!retval.isOK()) {
615            return retval;
616        }
617        
618        {    
619            final var _diffAddFX_reqPtr = _diffAddFXRequest;
620            
621            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
622            if (retval.isOK()) {
623                retval = _diffAddFX_retval;
624            }
625        }
626        
627        {
628            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
629            if (retval.isOK()) {
630                retval = _diffSubFX_retval;
631            }
632        }
633        
634        return retval;
635    }
636    
637    /**
638     * Sets the control request for this mechanism.
639     *
640     * @param _diffAddFXRequest    Request PID to target position with a
641     *                             differential position setpoint.
642     * @return Status Code of the request.
643     */
644    public StatusCode setControl(DifferentialPositionDutyCycle _diffAddFXRequest)
645    {
646        var retval = StatusCode.OK;
647    
648        retval = beforeControl();
649        if (!retval.isOK()) {
650            return retval;
651        }
652        
653        {    
654            final var _diffAddFX_reqPtr = _diffAddFXRequest;
655            
656            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
657            if (retval.isOK()) {
658                retval = _diffAddFX_retval;
659            }
660        }
661        
662        {
663            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
664            if (retval.isOK()) {
665                retval = _diffSubFX_retval;
666            }
667        }
668        
669        return retval;
670    }
671    
672    /**
673     * Sets the control request for this mechanism.
674     *
675     * @param _diffAddFXRequest    Request PID to target position with a
676     *                             differential position setpoint
677     * @return Status Code of the request.
678     */
679    public StatusCode setControl(DifferentialPositionVoltage _diffAddFXRequest)
680    {
681        var retval = StatusCode.OK;
682    
683        retval = beforeControl();
684        if (!retval.isOK()) {
685            return retval;
686        }
687        
688        {    
689            final var _diffAddFX_reqPtr = _diffAddFXRequest;
690            
691            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
692            if (retval.isOK()) {
693                retval = _diffAddFX_retval;
694            }
695        }
696        
697        {
698            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
699            if (retval.isOK()) {
700                retval = _diffSubFX_retval;
701            }
702        }
703        
704        return retval;
705    }
706    
707    /**
708     * Sets the control request for this mechanism.
709     *
710     * @param _diffAddFXRequest    Request PID to target velocity with a
711     *                             differential position setpoint.
712     * @return Status Code of the request.
713     */
714    public StatusCode setControl(DifferentialVelocityDutyCycle _diffAddFXRequest)
715    {
716        var retval = StatusCode.OK;
717    
718        retval = beforeControl();
719        if (!retval.isOK()) {
720            return retval;
721        }
722        
723        {    
724            final var _diffAddFX_reqPtr = _diffAddFXRequest;
725            
726            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
727            if (retval.isOK()) {
728                retval = _diffAddFX_retval;
729            }
730        }
731        
732        {
733            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
734            if (retval.isOK()) {
735                retval = _diffSubFX_retval;
736            }
737        }
738        
739        return retval;
740    }
741    
742    /**
743     * Sets the control request for this mechanism.
744     *
745     * @param _diffAddFXRequest    Request PID to target velocity with a
746     *                             differential position setpoint.
747     * @return Status Code of the request.
748     */
749    public StatusCode setControl(DifferentialVelocityVoltage _diffAddFXRequest)
750    {
751        var retval = StatusCode.OK;
752    
753        retval = beforeControl();
754        if (!retval.isOK()) {
755            return retval;
756        }
757        
758        {    
759            final var _diffAddFX_reqPtr = _diffAddFXRequest;
760            
761            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
762            if (retval.isOK()) {
763                retval = _diffAddFX_retval;
764            }
765        }
766        
767        {
768            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
769            if (retval.isOK()) {
770                retval = _diffSubFX_retval;
771            }
772        }
773        
774        return retval;
775    }
776    
777    /**
778     * Sets the control request for this mechanism.
779     *
780     * @param _diffAddFXRequest    Requests Motion Magic® to target a final position
781     *                             using a motion profile, and PID to a differential
782     *                             position setpoint.
783     * @return Status Code of the request.
784     */
785    public StatusCode setControl(DifferentialMotionMagicDutyCycle _diffAddFXRequest)
786    {
787        var retval = StatusCode.OK;
788    
789        retval = beforeControl();
790        if (!retval.isOK()) {
791            return retval;
792        }
793        
794        {    
795            final var _diffAddFX_reqPtr = _diffAddFXRequest;
796            
797            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
798            if (retval.isOK()) {
799                retval = _diffAddFX_retval;
800            }
801        }
802        
803        {
804            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
805            if (retval.isOK()) {
806                retval = _diffSubFX_retval;
807            }
808        }
809        
810        return retval;
811    }
812    
813    /**
814     * Sets the control request for this mechanism.
815     *
816     * @param _diffAddFXRequest    Requests Motion Magic® to target a final position
817     *                             using a motion profile, and PID to a differential
818     *                             position setpoint.
819     * @return Status Code of the request.
820     */
821    public StatusCode setControl(DifferentialMotionMagicVoltage _diffAddFXRequest)
822    {
823        var retval = StatusCode.OK;
824    
825        retval = beforeControl();
826        if (!retval.isOK()) {
827            return retval;
828        }
829        
830        {    
831            final var _diffAddFX_reqPtr = _diffAddFXRequest;
832            
833            final var _diffAddFX_retval = _diffAddFX.setControl(_diffAddFX_reqPtr);
834            if (retval.isOK()) {
835                retval = _diffAddFX_retval;
836            }
837        }
838        
839        {
840            final var _diffSubFX_retval = _diffSubFX.setControl(_diffFollow);
841            if (retval.isOK()) {
842                retval = _diffSubFX_retval;
843            }
844        }
845        
846        return retval;
847    }
848    
849}