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