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.phoenixpro.hardware;
008
009import java.util.ArrayList;
010import java.util.concurrent.locks.Lock;
011import java.util.concurrent.locks.ReentrantLock;
012
013import com.ctre.phoenix6.StatusCode;
014import com.ctre.phoenixpro.StatusSignalValue;
015import com.ctre.phoenixpro.configs.MotorOutputConfigs;
016import com.ctre.phoenixpro.controls.ControlRequest;
017import com.ctre.phoenixpro.controls.DutyCycleOut;
018import com.ctre.phoenixpro.controls.NeutralOut;
019import com.ctre.phoenixpro.controls.VoltageOut;
020import com.ctre.phoenixpro.hardware.core.CoreTalonFX;
021import com.ctre.phoenix6.jni.PlatformJNI;
022import com.ctre.phoenixpro.signals.InvertedValue;
023import com.ctre.phoenixpro.sim.DeviceType;
024import com.ctre.phoenixpro.wpiutils.AutoFeedEnable;
025import com.ctre.phoenixpro.wpiutils.CallbackHelper;
026import com.ctre.phoenixpro.wpiutils.MotorSafetyImplem;
027
028import edu.wpi.first.wpilibj.motorcontrol.MotorController;
029import edu.wpi.first.wpilibj.simulation.CallbackStore;
030import edu.wpi.first.wpilibj.simulation.SimDeviceSim;
031import edu.wpi.first.hal.HAL;
032import edu.wpi.first.hal.HALValue;
033import edu.wpi.first.hal.SimBoolean;
034import edu.wpi.first.hal.SimDevice;
035import edu.wpi.first.hal.SimDouble;
036import edu.wpi.first.hal.HAL.SimPeriodicBeforeCallback;
037import edu.wpi.first.hal.SimDevice.Direction;
038import edu.wpi.first.hal.simulation.SimDeviceDataJNI;
039import edu.wpi.first.hal.simulation.SimValueCallback;
040import edu.wpi.first.util.sendable.Sendable;
041import edu.wpi.first.util.sendable.SendableBuilder;
042import edu.wpi.first.util.sendable.SendableRegistry;
043
044/**
045 *
046 * @deprecated Classes in the phoenixpro package will be removed in 2024.
047 *             Users should instead use classes from the phoenix6 package.
048 */
049@Deprecated(forRemoval = true)
050public class TalonFX extends CoreTalonFX implements MotorController, Sendable, AutoCloseable {
051    /**
052     * The StatusSignalValue getters are copies so that calls
053     * to the WPI interface do not update any references
054     */
055    private final StatusSignalValue<Double> m_dutyCycle = getDutyCycle().clone();
056
057    private final DutyCycleOut m_setterControl = new DutyCycleOut(0);
058    private final NeutralOut m_neutralControl = new NeutralOut();
059    private final VoltageOut m_voltageControl = new VoltageOut(0);
060
061    private final MotorOutputConfigs m_configs = new MotorOutputConfigs();
062
063    private String m_description;
064
065    private static final DeviceType kSimDeviceType = DeviceType.PRO_TalonFXType;
066
067    private SimDevice m_simMotor;
068    private SimDouble m_simSupplyVoltage;
069    private SimDouble m_simDutyCycle;
070    private SimDouble m_simMotorVoltage;
071    private SimDouble m_simTorqueCurrent;
072    private SimDouble m_simSupplyCurrent;
073
074    private SimDevice m_simForwardLimit;
075    private SimBoolean m_simForwardLimitValue;
076
077    private SimDevice m_simReverseLimit;
078    private SimBoolean m_simReverseLimitValue;
079
080    private SimDevice m_simRotor;
081    private SimDouble m_simRotorPos;
082    private SimDouble m_simRotorRawPos;
083    private SimDouble m_simRotorVel;
084
085    // callbacks to register
086    private final SimValueCallback m_onValueChangedCallback = new OnValueChangedCallback();
087    private final Runnable m_onPeriodicCallback = new OnPeriodicCallback();
088
089    // returned registered callbacks
090    private final ArrayList<CallbackStore> m_simValueChangedCallbacks = new ArrayList<CallbackStore>();
091    private SimPeriodicBeforeCallback m_simPeriodicBeforeCallback = null;
092
093    /**
094     * The default motor safety timeout IF calling application
095     * enables the feature.
096     */
097    public static final double kDefaultSafetyExpiration = 0.1;
098
099    /**
100     * Late-constructed motor safety, which ensures feature is off unless calling
101     * applications explicitly enables it.
102     */
103    private MotorSafetyImplem m_motorSafety = null;
104    private final Lock m_lockMotorSaf = new ReentrantLock();
105    private double m_motSafeExpiration = kDefaultSafetyExpiration;
106
107    /**
108     * Constructs a new Talon FX motor controller object.
109     * <p>
110     * Constructs the device using the default CAN bus for the system:
111     * <ul>
112     *   <li>"rio" on roboRIO
113     *   <li>"can0" on Linux
114     *   <li>"*" on Windows
115     * </ul>
116     *
117     * @param deviceId ID of the device, as configured in Phoenix Tuner.
118     *
119     * @deprecated Classes in the phoenixpro package will be removed in 2024.
120     *             Users should instead use classes from the phoenix6 package.
121     */
122    @Deprecated(forRemoval = true)
123    public TalonFX(int deviceId) {
124        this(deviceId, "");
125    }
126
127    /**
128     * Constructs a new Talon FX motor controller object.
129     *
130     * @param deviceId ID of the device, as configured in Phoenix Tuner.
131     * @param canbus   Name of the CAN bus this device is on. Possible CAN bus
132     *                 strings are:
133     *                 <ul>
134     *                   <li>"rio" for the native roboRIO CAN bus
135     *                   <li>CANivore name or serial number
136     *                   <li>SocketCAN interface (non-FRC Linux only)
137     *                   <li>"*" for any CANivore seen by the program
138     *                   <li>empty string (default) to select the default for the
139     *                       system:
140     *                   <ul>
141     *                     <li>"rio" on roboRIO
142     *                     <li>"can0" on Linux
143     *                     <li>"*" on Windows
144     *                   </ul>
145     *                 </ul>
146     *
147     * @deprecated Classes in the phoenixpro package will be removed in 2024.
148     *             Users should instead use classes from the phoenix6 package.
149     */
150    @Deprecated(forRemoval = true)
151    public TalonFX(int deviceId, String canbus) {
152        super(deviceId, canbus);
153
154        m_description = "Talon FX (Pro) " + deviceId;
155        SendableRegistry.addLW(this, "Talon FX (Pro) ", deviceId);
156
157        m_simMotor = SimDevice.create("CANMotor:Talon FX (Pro)", deviceId);
158
159        final String base = "Talon FX (Pro)[" + deviceId + "]/";
160        m_simRotor = SimDevice.create("CANEncoder:" + base + "Rotor Sensor");
161        m_simForwardLimit = SimDevice.create("CANDIO:" + base + "Fwd Limit");
162        m_simReverseLimit = SimDevice.create("CANDIO:" + base + "Rev Limit");
163
164        if (m_simMotor != null) {
165            /* First make sure our simulated device gets enabled */
166            AutoFeedEnable.getInstance();
167            m_simPeriodicBeforeCallback = HAL.registerSimPeriodicBeforeCallback(m_onPeriodicCallback);
168
169            m_simSupplyVoltage = m_simMotor.createDouble("supplyVoltage", Direction.kInput, 12.0);
170
171            m_simDutyCycle = m_simMotor.createDouble("dutyCycle", Direction.kOutput, 0);
172            m_simMotorVoltage = m_simMotor.createDouble("motorVoltage", Direction.kOutput, 0);
173            m_simTorqueCurrent = m_simMotor.createDouble("torqueCurrent", Direction.kOutput, 0);
174            m_simSupplyCurrent = m_simMotor.createDouble("supplyCurrent", Direction.kOutput, 0);
175
176            final SimDeviceSim sim = new SimDeviceSim("CANMotor:Talon FX (Pro)");
177            m_simValueChangedCallbacks
178                    .add(sim.registerValueChangedCallback(m_simSupplyVoltage, m_onValueChangedCallback, true));
179        }
180        if (m_simRotor != null) {
181            m_simRotorPos = m_simRotor.createDouble("position", Direction.kOutput, 0);
182
183            m_simRotorRawPos = m_simRotor.createDouble("rawPositionInput", Direction.kInput, 0);
184            m_simRotorVel = m_simRotor.createDouble("velocity", Direction.kInput, 0);
185
186            final SimDeviceSim sim = new SimDeviceSim("CANEncoder:" + base + "Rotor Sensor");
187            m_simValueChangedCallbacks
188                    .add(sim.registerValueChangedCallback(m_simRotorRawPos, m_onValueChangedCallback, true));
189            m_simValueChangedCallbacks
190                    .add(sim.registerValueChangedCallback(m_simRotorVel, m_onValueChangedCallback, true));
191        }
192        if (m_simForwardLimit != null) {
193            m_simForwardLimit.createBoolean("init", Direction.kOutput, true);
194            m_simForwardLimit.createBoolean("input", Direction.kOutput, true);
195
196            m_simForwardLimitValue = m_simForwardLimit.createBoolean("value", Direction.kBidir, false);
197
198            final SimDeviceSim sim = new SimDeviceSim("CANDIO:" + base + "Fwd Limit");
199            m_simValueChangedCallbacks
200                    .add(sim.registerValueChangedCallback(m_simForwardLimitValue, m_onValueChangedCallback, true));
201        }
202        if (m_simReverseLimit != null) {
203            m_simReverseLimit.createBoolean("init", Direction.kOutput, true);
204            m_simReverseLimit.createBoolean("input", Direction.kOutput, true);
205
206            m_simReverseLimitValue = m_simReverseLimit.createBoolean("value", Direction.kBidir, false);
207
208            final SimDeviceSim sim = new SimDeviceSim("CANDIO:" + base + "Rev Limit");
209            m_simValueChangedCallbacks
210                    .add(sim.registerValueChangedCallback(m_simReverseLimitValue, m_onValueChangedCallback, true));
211        }
212    }
213
214    // ----- Callbacks for Sim ----- //
215    private class OnValueChangedCallback implements SimValueCallback {
216        @Override
217        public void callback(String name, int handle, int direction, HALValue value) {
218            String deviceName = SimDeviceDataJNI.getSimDeviceName(SimDeviceDataJNI.getSimValueDeviceHandle(handle));
219            String physType = deviceName + ":" + name;
220            PlatformJNI.JNI_SimSetPhysicsInput(kSimDeviceType.value, getDeviceID(),
221                    physType, CallbackHelper.getRawValue(value));
222        }
223    }
224
225    private class OnPeriodicCallback implements Runnable {
226        @Override
227        public void run() {
228            double value = 0;
229            int err = 0;
230
231            final int deviceID = getDeviceID();
232
233            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "SupplyVoltage");
234            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
235            if (err == 0) {
236                m_simSupplyVoltage.set(value);
237            }
238            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "DutyCycle");
239            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
240            if (err == 0) {
241                m_simDutyCycle.set(value);
242            }
243            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "MotorVoltage");
244            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
245            if (err == 0) {
246                m_simMotorVoltage.set(value);
247            }
248            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "TorqueCurrent");
249            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
250            if (err == 0) {
251                m_simTorqueCurrent.set(value);
252            }
253            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "SupplyCurrent");
254            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
255            if (err == 0) {
256                m_simSupplyCurrent.set(value);
257            }
258            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "RotorPosition");
259            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
260            if (err == 0) {
261                m_simRotorPos.set(value);
262            }
263            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "RawRotorPosition");
264            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
265            if (err == 0) {
266                m_simRotorRawPos.set(value);
267            }
268            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "RotorVelocity");
269            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
270            if (err == 0) {
271                m_simRotorVel.set(value);
272            }
273            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "ForwardLimit");
274            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
275            if (err == 0) {
276                m_simForwardLimitValue.set((int) value != 0);
277            }
278            value = PlatformJNI.JNI_SimGetPhysicsValue(kSimDeviceType.value, deviceID, "ReverseLimit");
279            err = PlatformJNI.JNI_SimGetLastError(kSimDeviceType.value, deviceID);
280            if (err == 0) {
281                m_simReverseLimitValue.set((int) value != 0);
282            }
283        }
284    }
285
286    // ------- AutoCloseable ----- //
287    @Override
288    public void close() {
289        SendableRegistry.remove(this);
290        if (m_simPeriodicBeforeCallback != null) {
291            m_simPeriodicBeforeCallback.close();
292            m_simPeriodicBeforeCallback = null;
293        }
294        if (m_simMotor != null) {
295            m_simMotor.close();
296            m_simMotor = null;
297        }
298        if (m_simRotor != null) {
299            m_simRotor.close();
300            m_simRotor = null;
301        }
302        if (m_simForwardLimit != null) {
303            m_simForwardLimit.close();
304            m_simForwardLimit = null;
305        }
306        if (m_simReverseLimit != null) {
307            m_simReverseLimit.close();
308            m_simReverseLimit = null;
309        }
310
311        for (var callback : m_simValueChangedCallbacks) {
312            callback.close();
313        }
314        m_simValueChangedCallbacks.clear();
315    }
316
317    // ------ set/get routines for WPILIB interfaces ------//
318    /**
319     * Common interface for setting the speed of a motor controller.
320     *
321     * @param speed The speed to set. Value should be between -1.0 and 1.0.
322     */
323    @Override
324    public void set(double speed) {
325        feed();
326        setControl(m_setterControl.withOutput(speed));
327    }
328
329    /**
330     * Common interface for seting the direct voltage output of a motor controller.
331     *
332     * @param volts The voltage to output.
333     */
334    @Override
335    public void setVoltage(double volts) {
336        feed();
337        setControl(m_voltageControl.withOutput(volts));
338    }
339
340    /**
341     * Common interface for getting the current set speed of a motor controller.
342     *
343     * @return The current set speed. Value is between -1.0 and 1.0.
344     */
345    @Override
346    public double get() {
347        return m_dutyCycle.refresh().getValue();
348    }
349
350    // ---------Intercept CTRE calls for motor safety ---------//
351    @Override
352    protected StatusCode setControlPrivate(ControlRequest request) {
353        /* intercept the control setter and feed motor-safety */
354        feed();
355        return super.setControlPrivate(request);
356    }
357
358    // ----------------------- turn-motor-off routines-------------------//
359    /**
360     * Common interface for disabling a motor controller.
361     */
362    @Override
363    public void disable() {
364        setControl(m_neutralControl);
365    }
366
367    /**
368     * Common interface to stop motor movement until set is called again.
369     */
370    @Override
371    public void stopMotor() {
372        disable();
373    }
374
375    // ----------------------- Invert routines -------------------//
376    /**
377     * Common interface for inverting direction of a motor controller.
378     *
379     * @param isInverted The state of inversion, true is inverted.
380     */
381    @Override
382    public void setInverted(boolean isInverted) {
383        /* First read the configs so they're up-to-date */
384        getConfigurator().refresh(m_configs);
385        /* Then set the invert config to the appropriate value */
386        m_configs.Inverted = isInverted ? InvertedValue.Clockwise_Positive : InvertedValue.CounterClockwise_Positive;
387        getConfigurator().apply(m_configs);
388    }
389
390    /**
391     * Common interface for returning the inversion state of a motor controller.
392     *
393     * @return The state of the inversion, true is inverted.
394     */
395    @Override
396    public boolean getInverted() {
397        /* Read the configs so they're up-to-date */
398        getConfigurator().refresh(m_configs);
399        /* Return true if we're Clockwise_Positive */
400        return m_configs.Inverted == InvertedValue.Clockwise_Positive;
401    }
402
403    // ---- Sendable -------//
404    @Override
405    public void initSendable(SendableBuilder builder) {
406        builder.setSmartDashboardType("Motor Controller");
407        builder.setActuator(true);
408        builder.setSafeState(this::stopMotor);
409        builder.addDoubleProperty("Value", this::get, this::set);
410    }
411
412    /**
413     * @return Description of motor controller
414     */
415    public String getDescription() {
416        return m_description;
417    }
418
419    /* ----- Motor Safety ----- */
420    /** caller must lock appropriately */
421    private MotorSafetyImplem GetMotorSafety() {
422        if (m_motorSafety == null) {
423            /* newly created MS object */
424            m_motorSafety = new MotorSafetyImplem(this, getDescription());
425            /* apply the expiration timeout */
426            m_motorSafety.setExpiration(m_motSafeExpiration);
427        }
428        return m_motorSafety;
429    }
430
431    /**
432     * Feed the motor safety object.
433     * <p>
434     * Resets the timer on this object that is used to do the timeouts.
435     */
436    public void feed() {
437        m_lockMotorSaf.lock();
438        try {
439            if (m_motorSafety == null) {
440                /* do nothing, MS features were never enabled */
441            } else {
442                GetMotorSafety().feed();
443            }
444        } finally {
445            m_lockMotorSaf.unlock();
446        }
447    }
448
449    /**
450     * Set the expiration time for the corresponding motor safety object.
451     *
452     * @param expirationTime The timeout value in seconds.
453     */
454    public void setExpiration(double expirationTime) {
455        m_lockMotorSaf.lock();
456        try {
457            /* save the value for if/when we do create the MS object */
458            m_motSafeExpiration = expirationTime;
459            /* apply it only if MS object exists */
460            if (m_motorSafety == null) {
461                /* do nothing, MS features were never enabled */
462            } else {
463                /* this call will trigger creating a registered MS object */
464                GetMotorSafety().setExpiration(m_motSafeExpiration);
465            }
466        } finally {
467            m_lockMotorSaf.unlock();
468        }
469    }
470
471    /**
472     * Retrieve the timeout value for the corresponding motor safety object.
473     *
474     * @return the timeout value in seconds.
475     */
476    public double getExpiration() {
477        m_lockMotorSaf.lock();
478        try {
479            /* return the intended expiration time */
480            return m_motSafeExpiration;
481        } finally {
482            m_lockMotorSaf.unlock();
483        }
484    }
485
486    /**
487     * Determine of the motor is still operating or has timed out.
488     *
489     * @return a true value if the motor is still operating normally and hasn't
490     *         timed out.
491     */
492    public boolean isAlive() {
493        m_lockMotorSaf.lock();
494        try {
495            if (m_motorSafety == null) {
496                /* MC is alive - MS features were never enabled to neutral the MC. */
497                return true;
498            } else {
499                return GetMotorSafety().isAlive();
500            }
501        } finally {
502            m_lockMotorSaf.unlock();
503        }
504    }
505
506    /**
507     * Enable/disable motor safety for this device.
508     * <p>
509     * Turn on and off the motor safety option for this PWM object.
510     *
511     * @param enabled True if motor safety is enforced for this object
512     */
513    public void setSafetyEnabled(boolean enabled) {
514        m_lockMotorSaf.lock();
515        try {
516            if (m_motorSafety == null && !enabled) {
517                /*
518                 * Caller wants to disable MS,
519                 * but MS features were nevere enabled,
520                 * so it doesn't need to be disabled.
521                 */
522            } else {
523                /* MS will be created if it does not exist */
524                GetMotorSafety().setSafetyEnabled(enabled);
525            }
526        } finally {
527            m_lockMotorSaf.unlock();
528        }
529    }
530
531    /**
532     * Return the state of the motor safety enabled flag.
533     * <p>
534     * Return if the motor safety is currently enabled for this device.
535     *
536     * @return True if motor safety is enforced for this device
537     */
538    public boolean isSafetyEnabled() {
539        m_lockMotorSaf.lock();
540        try {
541            if (m_motorSafety == null) {
542                /* MS features were never enabled. */
543                return false;
544            } else {
545                return GetMotorSafety().isSafetyEnabled();
546            }
547        } finally {
548            m_lockMotorSaf.unlock();
549        }
550    }
551}