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