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.controls;
008
009import com.ctre.phoenix6.StatusCode;
010import com.ctre.phoenix6.controls.jni.ControlJNI;
011
012import edu.wpi.first.units.*;
013import edu.wpi.first.units.measure.*;
014import static edu.wpi.first.units.Units.*;
015
016import java.util.HashMap;
017import java.util.Map;
018
019/**
020 * Requires Phoenix Pro;
021 * Request a specified motor current (field oriented control).
022 * <p>
023 * This control request will drive the motor to the requested motor (stator) current value.  This leverages
024 * field oriented control (FOC), which means greater peak power than what is documented.  This scales to
025 * torque based on Motor's kT constant.
026 */
027public class TorqueCurrentFOC extends ControlRequest implements Cloneable
028{
029    /**
030     * Amount of motor current in Amperes
031     */
032    public double Output;
033    /**
034     * The maximum absolute motor output that can be applied, which effectively
035     * limits the velocity. For example, 0.50 means no more than 50% output in
036     * either direction.  This is useful for preventing the motor from spinning to
037     * its terminal velocity when there is no external torque applied unto the
038     * rotor.  Note this is absolute maximum, so the value should be between zero
039     * and one.
040     */
041    public double MaxAbsDutyCycle = 1.0;
042    /**
043     * Deadband in Amperes.  If torque request is within deadband, the bridge output
044     * is neutral. If deadband is set to zero then there is effectively no deadband.
045     * Note if deadband is zero, a free spinning motor will spin for quite a while
046     * as the firmware attempts to hold the motor's bemf. If user expects motor to
047     * cease spinning quickly with a demand of zero, we recommend a deadband of one
048     * Ampere. This value will be converted to an integral value of amps.
049     */
050    public double Deadband = 0.0;
051    /**
052     * Set to true to coast the rotor when output is zero (or within deadband).  Set
053     * to false to use the NeutralMode configuration setting (default). This flag
054     * exists to provide the fundamental behavior of this control when output is
055     * zero, which is to provide 0A (zero torque).
056     */
057    public boolean OverrideCoastDurNeutral = false;
058    /**
059     * Set to true to force forward limiting.  This allows users to use other limit
060     * switch sensors connected to robot controller.  This also allows use of active
061     * sensors that require external power.
062     */
063    public boolean LimitForwardMotion = false;
064    /**
065     * Set to true to force reverse limiting.  This allows users to use other limit
066     * switch sensors connected to robot controller.  This also allows use of active
067     * sensors that require external power.
068     */
069    public boolean LimitReverseMotion = false;
070    /**
071     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
072     * LimitReverseMotion parameters, instead allowing motion.
073     * <p>
074     * This can be useful on mechanisms such as an intake/feeder, where a limit
075     * switch stops motion while intaking but should be ignored when feeding to a
076     * shooter.
077     * <p>
078     * The hardware limit faults and Forward/ReverseLimit signals will still report
079     * the values of the limit switches regardless of this parameter.
080     */
081    public boolean IgnoreHardwareLimits = false;
082    /**
083     * Set to true to delay applying this control request until a timesync boundary
084     * (requires Phoenix Pro and CANivore). This eliminates the impact of
085     * nondeterministic network delays in exchange for a larger but deterministic
086     * control latency.
087     * <p>
088     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
089     * Additionally, when this is enabled, the UpdateFreqHz of this request should
090     * be set to 0 Hz.
091     */
092    public boolean UseTimesync = false;
093
094    /**
095     * The period at which this control will update at.
096     * This is designated in Hertz, with a minimum of 20 Hz
097     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
098     * <p>
099     * If this field is set to 0 Hz, the control request will
100     * be sent immediately as a one-shot frame. This may be useful
101     * for advanced applications that require outputs to be
102     * synchronized with data acquisition. In this case, we
103     * recommend not exceeding 50 ms between control calls.
104     */
105    public double UpdateFreqHz = 100;
106
107    /**
108     * Requires Phoenix Pro;
109     * Request a specified motor current (field oriented control).
110     * <p>
111     * This control request will drive the motor to the requested motor (stator)
112     * current value.  This leverages field oriented control (FOC), which means
113     * greater peak power than what is documented.  This scales to torque based on
114     * Motor's kT constant.
115     * 
116     * @param Output    Amount of motor current in Amperes
117     */
118    public TorqueCurrentFOC(double Output)
119    {
120        super("TorqueCurrentFOC");
121        this.Output = Output;
122    }
123
124    /**
125     * Requires Phoenix Pro;
126     * Request a specified motor current (field oriented control).
127     * <p>
128     * This control request will drive the motor to the requested motor (stator)
129     * current value.  This leverages field oriented control (FOC), which means
130     * greater peak power than what is documented.  This scales to torque based on
131     * Motor's kT constant.
132     * 
133     * @param Output    Amount of motor current in Amperes
134     */
135    public TorqueCurrentFOC(Current Output)
136    {
137        this(Output.in(Amps));
138    }
139
140    @Override
141    public String toString()
142    {
143        String ss = "Control: TorqueCurrentFOC\n";
144        ss += "    Output: " + Output + " A" + "\n";
145        ss += "    MaxAbsDutyCycle: " + MaxAbsDutyCycle + " fractional" + "\n";
146        ss += "    Deadband: " + Deadband + " A" + "\n";
147        ss += "    OverrideCoastDurNeutral: " + OverrideCoastDurNeutral + "\n";
148        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
149        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
150        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
151        ss += "    UseTimesync: " + UseTimesync + "\n";
152        return ss;
153    }
154
155    @Override
156    public StatusCode sendRequest(String network, int deviceHash)
157    {
158        return StatusCode.valueOf(ControlJNI.JNI_RequestControlTorqueCurrentFOC(
159                network, deviceHash, UpdateFreqHz, Output, MaxAbsDutyCycle, Deadband, OverrideCoastDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, UseTimesync));
160    }
161
162    /**
163     * Gets information about this control request.
164     *
165     * @return Map of control parameter names and corresponding applied values
166     */
167    @Override
168    public Map<String, String> getControlInfo()
169    {
170        var controlInfo = new HashMap<String, String>();
171        controlInfo.put("Name", getName());
172        controlInfo.put("Output", String.valueOf(this.Output));
173        controlInfo.put("MaxAbsDutyCycle", String.valueOf(this.MaxAbsDutyCycle));
174        controlInfo.put("Deadband", String.valueOf(this.Deadband));
175        controlInfo.put("OverrideCoastDurNeutral", String.valueOf(this.OverrideCoastDurNeutral));
176        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
177        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
178        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
179        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
180        return controlInfo;
181    }
182    
183    /**
184     * Modifies this Control Request's Output parameter and returns itself for
185     * method-chaining and easier to use request API.
186     * <p>
187     * Amount of motor current in Amperes
188     *
189     * @param newOutput Parameter to modify
190     * @return Itself
191     */
192    public TorqueCurrentFOC withOutput(double newOutput)
193    {
194        Output = newOutput;
195        return this;
196    }
197    
198    /**
199     * Modifies this Control Request's Output parameter and returns itself for
200     * method-chaining and easier to use request API.
201     * <p>
202     * Amount of motor current in Amperes
203     *
204     * @param newOutput Parameter to modify
205     * @return Itself
206     */
207    public TorqueCurrentFOC withOutput(Current newOutput)
208    {
209        Output = newOutput.in(Amps);
210        return this;
211    }
212    
213    /**
214     * Helper method to get this Control Request's Output parameter converted
215     * to a unit type. If not using the Java units library, {@link #Output}
216     * can be accessed directly instead.
217     * <p>
218     * Amount of motor current in Amperes
219     *
220     * @return Output
221     */
222    public Current getOutputMeasure()
223    {
224        return Amps.of(Output);
225    }
226    
227    /**
228     * Modifies this Control Request's MaxAbsDutyCycle parameter and returns itself for
229     * method-chaining and easier to use request API.
230     * <p>
231     * The maximum absolute motor output that can be applied, which effectively
232     * limits the velocity. For example, 0.50 means no more than 50% output in
233     * either direction.  This is useful for preventing the motor from spinning to
234     * its terminal velocity when there is no external torque applied unto the
235     * rotor.  Note this is absolute maximum, so the value should be between zero
236     * and one.
237     *
238     * @param newMaxAbsDutyCycle Parameter to modify
239     * @return Itself
240     */
241    public TorqueCurrentFOC withMaxAbsDutyCycle(double newMaxAbsDutyCycle)
242    {
243        MaxAbsDutyCycle = newMaxAbsDutyCycle;
244        return this;
245    }
246    
247    /**
248     * Modifies this Control Request's Deadband parameter and returns itself for
249     * method-chaining and easier to use request API.
250     * <p>
251     * Deadband in Amperes.  If torque request is within deadband, the bridge output
252     * is neutral. If deadband is set to zero then there is effectively no deadband.
253     * Note if deadband is zero, a free spinning motor will spin for quite a while
254     * as the firmware attempts to hold the motor's bemf. If user expects motor to
255     * cease spinning quickly with a demand of zero, we recommend a deadband of one
256     * Ampere. This value will be converted to an integral value of amps.
257     *
258     * @param newDeadband Parameter to modify
259     * @return Itself
260     */
261    public TorqueCurrentFOC withDeadband(double newDeadband)
262    {
263        Deadband = newDeadband;
264        return this;
265    }
266    
267    /**
268     * Modifies this Control Request's Deadband parameter and returns itself for
269     * method-chaining and easier to use request API.
270     * <p>
271     * Deadband in Amperes.  If torque request is within deadband, the bridge output
272     * is neutral. If deadband is set to zero then there is effectively no deadband.
273     * Note if deadband is zero, a free spinning motor will spin for quite a while
274     * as the firmware attempts to hold the motor's bemf. If user expects motor to
275     * cease spinning quickly with a demand of zero, we recommend a deadband of one
276     * Ampere. This value will be converted to an integral value of amps.
277     *
278     * @param newDeadband Parameter to modify
279     * @return Itself
280     */
281    public TorqueCurrentFOC withDeadband(Current newDeadband)
282    {
283        Deadband = newDeadband.in(Amps);
284        return this;
285    }
286    
287    /**
288     * Helper method to get this Control Request's Deadband parameter converted
289     * to a unit type. If not using the Java units library, {@link #Deadband}
290     * can be accessed directly instead.
291     * <p>
292     * Deadband in Amperes.  If torque request is within deadband, the bridge output
293     * is neutral. If deadband is set to zero then there is effectively no deadband.
294     * Note if deadband is zero, a free spinning motor will spin for quite a while
295     * as the firmware attempts to hold the motor's bemf. If user expects motor to
296     * cease spinning quickly with a demand of zero, we recommend a deadband of one
297     * Ampere. This value will be converted to an integral value of amps.
298     *
299     * @return Deadband
300     */
301    public Current getDeadbandMeasure()
302    {
303        return Amps.of(Deadband);
304    }
305    
306    /**
307     * Modifies this Control Request's OverrideCoastDurNeutral parameter and returns itself for
308     * method-chaining and easier to use request API.
309     * <p>
310     * Set to true to coast the rotor when output is zero (or within deadband).  Set
311     * to false to use the NeutralMode configuration setting (default). This flag
312     * exists to provide the fundamental behavior of this control when output is
313     * zero, which is to provide 0A (zero torque).
314     *
315     * @param newOverrideCoastDurNeutral Parameter to modify
316     * @return Itself
317     */
318    public TorqueCurrentFOC withOverrideCoastDurNeutral(boolean newOverrideCoastDurNeutral)
319    {
320        OverrideCoastDurNeutral = newOverrideCoastDurNeutral;
321        return this;
322    }
323    
324    /**
325     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
326     * method-chaining and easier to use request API.
327     * <p>
328     * Set to true to force forward limiting.  This allows users to use other limit
329     * switch sensors connected to robot controller.  This also allows use of active
330     * sensors that require external power.
331     *
332     * @param newLimitForwardMotion Parameter to modify
333     * @return Itself
334     */
335    public TorqueCurrentFOC withLimitForwardMotion(boolean newLimitForwardMotion)
336    {
337        LimitForwardMotion = newLimitForwardMotion;
338        return this;
339    }
340    
341    /**
342     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
343     * method-chaining and easier to use request API.
344     * <p>
345     * Set to true to force reverse limiting.  This allows users to use other limit
346     * switch sensors connected to robot controller.  This also allows use of active
347     * sensors that require external power.
348     *
349     * @param newLimitReverseMotion Parameter to modify
350     * @return Itself
351     */
352    public TorqueCurrentFOC withLimitReverseMotion(boolean newLimitReverseMotion)
353    {
354        LimitReverseMotion = newLimitReverseMotion;
355        return this;
356    }
357    
358    /**
359     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
360     * method-chaining and easier to use request API.
361     * <p>
362     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
363     * LimitReverseMotion parameters, instead allowing motion.
364     * <p>
365     * This can be useful on mechanisms such as an intake/feeder, where a limit
366     * switch stops motion while intaking but should be ignored when feeding to a
367     * shooter.
368     * <p>
369     * The hardware limit faults and Forward/ReverseLimit signals will still report
370     * the values of the limit switches regardless of this parameter.
371     *
372     * @param newIgnoreHardwareLimits Parameter to modify
373     * @return Itself
374     */
375    public TorqueCurrentFOC withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits)
376    {
377        IgnoreHardwareLimits = newIgnoreHardwareLimits;
378        return this;
379    }
380    
381    /**
382     * Modifies this Control Request's UseTimesync parameter and returns itself for
383     * method-chaining and easier to use request API.
384     * <p>
385     * Set to true to delay applying this control request until a timesync boundary
386     * (requires Phoenix Pro and CANivore). This eliminates the impact of
387     * nondeterministic network delays in exchange for a larger but deterministic
388     * control latency.
389     * <p>
390     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
391     * Additionally, when this is enabled, the UpdateFreqHz of this request should
392     * be set to 0 Hz.
393     *
394     * @param newUseTimesync Parameter to modify
395     * @return Itself
396     */
397    public TorqueCurrentFOC withUseTimesync(boolean newUseTimesync)
398    {
399        UseTimesync = newUseTimesync;
400        return this;
401    }
402
403    /**
404     * Sets the period at which this control will update at.
405     * This is designated in Hertz, with a minimum of 20 Hz
406     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
407     * <p>
408     * If this field is set to 0 Hz, the control request will
409     * be sent immediately as a one-shot frame. This may be useful
410     * for advanced applications that require outputs to be
411     * synchronized with data acquisition. In this case, we
412     * recommend not exceeding 50 ms between control calls.
413     *
414     * @param newUpdateFreqHz Parameter to modify
415     * @return Itself
416     */
417    @Override
418    public TorqueCurrentFOC withUpdateFreqHz(double newUpdateFreqHz)
419    {
420        UpdateFreqHz = newUpdateFreqHz;
421        return this;
422    }
423
424    /**
425     * Sets the period at which this control will update at.
426     * This is designated in Hertz, with a minimum of 20 Hz
427     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
428     * <p>
429     * If this field is set to 0 Hz, the control request will
430     * be sent immediately as a one-shot frame. This may be useful
431     * for advanced applications that require outputs to be
432     * synchronized with data acquisition. In this case, we
433     * recommend not exceeding 50 ms between control calls.
434     *
435     * @param newUpdateFreqHz Parameter to modify
436     * @return Itself
437     */
438    public TorqueCurrentFOC withUpdateFreqHz(Frequency newUpdateFreqHz)
439    {
440        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
441        return this;
442    }
443
444    @Override
445    public TorqueCurrentFOC clone()
446    {
447        try {
448            return (TorqueCurrentFOC)super.clone();
449        } catch (CloneNotSupportedException ex) {
450            /* this should never happen */
451            throw new RuntimeException(ex);
452        }
453    }
454}
455