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.sim;
008
009import static edu.wpi.first.units.Units.*;
010
011import com.ctre.phoenix6.StatusCode;
012import com.ctre.phoenix6.hardware.core.CoreTalonFX;
013import com.ctre.phoenix6.hardware.TalonFX;
014import com.ctre.phoenix6.jni.PlatformJNI;
015
016import edu.wpi.first.units.measure.*;
017
018/**
019 * Class to control the state of a simulated {@link TalonFX}.
020 */
021public class TalonFXSimState {
022        private static final DeviceType kDevType = DeviceType.P6_TalonFXType;
023
024        private final int _id;
025
026        /**
027         * The orientation of the TalonFX relative to the robot chassis.
028         * <p>
029         * This value should not be changed based on the TalonFX invert.
030         * Rather, this value should be changed when the mechanical linkage
031         * between the TalonFX and the robot changes.
032         */
033        public ChassisReference Orientation;
034
035        /**
036         * Creates an object to control the state of the given {@link TalonFX}.
037         * <p>
038         * This constructor defaults to a counter-clockwise positive orientation
039         * relative to the robot chassis.
040         * <p>
041         * Note the recommended method of accessing simulation features is to use
042         * {@link TalonFX#getSimState}
043         *
044         * @param device Device to which this simulation state is attached
045         */
046        public TalonFXSimState(CoreTalonFX device) {
047                this(device, ChassisReference.CounterClockwise_Positive);
048        }
049        /**
050         * Creates an object to control the state of the given {@link TalonFX}.
051         * <p>
052         * Note the recommended method of accessing simulation features is to use
053         * {@link TalonFX#getSimState}
054         *
055         * @param device Device to which this simulation state is attached
056         * @param orientation Orientation of the device relative to the robot chassis
057         */
058        public TalonFXSimState(CoreTalonFX device, ChassisReference orientation) {
059                _id = device.getDeviceID();
060                Orientation = orientation;
061        }
062
063        /**
064         * Gets the last status code generated by a simulation function.
065         * <p>
066         * Not all functions return a status code but can potentially report errors.
067         * This function can be used to retrieve those status codes.
068         *
069         * @return Last status code generated by a simulation function
070         */
071        public StatusCode getLastStatusCode() {
072                return StatusCode.valueOf(PlatformJNI.JNI_SimGetLastError(kDevType.value, _id));
073        }
074
075        /**
076         * Gets the simulated output voltage of the motor.
077         *
078         * @return Voltage applied to the motor in Volts
079         */
080        public double getMotorVoltage() {
081                double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "MotorVoltage");
082                if (Orientation == ChassisReference.Clockwise_Positive) {
083                        value = -value;
084                }
085                return value;
086        }
087        /**
088         * Gets the simulated output voltage of the motor as a unit type.
089         *
090         * @return Voltage applied to the motor
091         */
092        public Voltage getMotorVoltageMeasure() {
093                return Volts.of(getMotorVoltage());
094        }
095
096        /**
097         * Gets the simulated output torque current of the motor.
098         * <p>
099         * Phoenix 6 simulation automatically calculates current.
100         *
101         * @return Torque current applied to the motor in Amperes
102         */
103        public double getTorqueCurrent() {
104                double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "TorqueCurrent");
105                if (Orientation == ChassisReference.Clockwise_Positive) {
106                        value = -value;
107                }
108                return value;
109        }
110        /**
111         * Gets the simulated output torque current of the motor as a unit type.
112         * <p>
113         * Phoenix 6 simulation automatically calculates current.
114         *
115         * @return Torque current applied to the motor
116         */
117        public Current getTorqueCurrentMeasure() {
118                return Amps.of(getTorqueCurrent());
119        }
120
121        /**
122         * Gets the simulated supply current of the TalonFX.
123         * <p>
124         * Phoenix 6 simulation automatically calculates current.
125         *
126         * @return Supply current of the TalonFX in Amperes
127         */
128        public double getSupplyCurrent() {
129                double value = PlatformJNI.JNI_SimGetPhysicsValue(kDevType.value, _id, "SupplyCurrent");
130                return value;
131        }
132        /**
133         * Gets the simulated supply current of the TalonFX as a unit type.
134         * <p>
135         * Phoenix 6 simulation automatically calculates current.
136         *
137         * @return Supply current of the TalonFX
138         */
139        public Current getSupplyCurrentMeasure() {
140                return Amps.of(getSupplyCurrent());
141        }
142
143        /**
144         * Sets the simulated supply voltage of the TalonFX.
145         * <p>
146         * The minimum allowed supply voltage is 4 V - values below this
147         * will be promoted to 4 V.
148         *
149         * @param volts The supply voltage in Volts
150         * @return Status code
151         */
152        public StatusCode setSupplyVoltage(double volts) {
153                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "SupplyVoltage", volts));
154        }
155        /**
156         * Sets the simulated supply voltage of the TalonFX.
157         * <p>
158         * The minimum allowed supply voltage is 4 V - values below this
159         * will be promoted to 4 V.
160         *
161         * @param voltage The supply voltage
162         * @return Status code
163         */
164        public StatusCode setSupplyVoltage(Voltage voltage) {
165                return setSupplyVoltage(voltage.in(Volts));
166        }
167
168        /**
169         * Sets the simulated forward limit switch of the TalonFX.
170         *
171         * @param closed Whether the limit switch is closed
172         * @return Status code
173         */
174        public StatusCode setForwardLimit(boolean closed) {
175                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "ForwardLimit", closed ? 1 : 0));
176        }
177        /**
178         * Sets the simulated reverse limit switch of the TalonFX.
179         *
180         * @param closed Whether the limit switch is closed
181         * @return Status code
182         */
183        public StatusCode setReverseLimit(boolean closed) {
184                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "ReverseLimit", closed ? 1 : 0));
185        }
186
187        /**
188         * Sets the simulated raw rotor position of the TalonFX. This is the position
189         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
190         * <p>
191         * Inputs to this function over time should be continuous, as user calls of {@link TalonFX#setPosition} will be accounted for in the callee.
192         * <p>
193         * The TalonFX integrates this to calculate the true reported rotor position.
194         * <p>
195         * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}.
196         * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API.
197         * Changes to {@code rawPositionInput} will be integrated into the emulated position.
198         * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor.
199         *
200         * @param rotations The raw position in rotations
201         * @return Status code
202         */
203        public StatusCode setRawRotorPosition(double rotations) {
204                if (Orientation == ChassisReference.Clockwise_Positive) {
205                        rotations = -rotations;
206                }
207                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RawRotorPosition", rotations));
208        }
209        /**
210         * Sets the simulated raw rotor position of the TalonFX. This is the position
211         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
212         * <p>
213         * Inputs to this function over time should be continuous, as user calls of {@link TalonFX#setPosition} will be accounted for in the callee.
214         * <p>
215         * The TalonFX integrates this to calculate the true reported rotor position.
216         * <p>
217         * When using the WPI Sim GUI, you will notice a readonly {@code position} and settable {@code rawPositionInput}.
218         * The readonly signal is the emulated position which will match self-test in Tuner and the hardware API.
219         * Changes to {@code rawPositionInput} will be integrated into the emulated position.
220         * This way a simulator can modify the position without overriding hardware API calls for home-ing the sensor.
221         *
222         * @param position The raw position
223         * @return Status code
224         */
225        public StatusCode setRawRotorPosition(Angle position) {
226                return setRawRotorPosition(position.in(Rotations));
227        }
228
229        /**
230         * Adds to the simulated rotor position of the TalonFX. This adds to the position
231         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
232         *
233         * @param dRotations The change in position in rotations
234         * @return Status code
235         */
236        public StatusCode addRotorPosition(double dRotations) {
237                if (Orientation == ChassisReference.Clockwise_Positive) {
238                        dRotations = -dRotations;
239                }
240                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "AddRotorPosition", dRotations));
241        }
242        /**
243         * Adds to the simulated rotor position of the TalonFX. This adds to the position
244         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
245         *
246         * @param dPosition The change in position
247         * @return Status code
248         */
249        public StatusCode addRotorPosition(Angle dPosition) {
250                return addRotorPosition(dPosition.in(Rotations));
251        }
252
253        /**
254         * Sets the simulated rotor velocity of the TalonFX. This is the velocity
255         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
256         *
257         * @param rps The new velocity in rotations per second
258         * @return Status code
259         */
260        public StatusCode setRotorVelocity(double rps) {
261                if (Orientation == ChassisReference.Clockwise_Positive) {
262                        rps = -rps;
263                }
264                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RotorVelocity", rps));
265        }
266        /**
267         * Sets the simulated rotor velocity of the TalonFX. This is the velocity
268         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
269         *
270         * @param velocity The new velocity
271         * @return Status code
272         */
273        public StatusCode setRotorVelocity(AngularVelocity velocity) {
274                return setRotorVelocity(velocity.in(RotationsPerSecond));
275        }
276
277        /**
278         * Sets the simulated rotor acceleration of the TalonFX. This is the acceleration
279         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
280         *
281         * @param rpss The new acceleration in rotations per second²
282         * @return Status code
283         */
284        public StatusCode setRotorAcceleration(double rpss) {
285                if (Orientation == ChassisReference.Clockwise_Positive) {
286                        rpss = -rpss;
287                }
288                return StatusCode.valueOf(PlatformJNI.JNI_SimSetPhysicsInput(kDevType.value, _id, "RotorAcceleration", rpss));
289        }
290        /**
291         * Sets the simulated rotor acceleration of the TalonFX. This is the acceleration
292         * of the rotor (before gear ratio) used for the RotorSensor feedback source.
293         *
294         * @param acceleration The new acceleration
295         * @return Status code
296         */
297        public StatusCode setRotorAcceleration(AngularAcceleration acceleration) {
298                return setRotorAcceleration(acceleration.in(RotationsPerSecondPerSecond));
299        }
300}