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 * Requests Motion Magic® to target a final position using a motion profile. 
022 * Users can optionally provide a duty cycle feedforward.
023 * <p>
024 * Motion Magic® produces a motion profile in real-time while attempting to honor the Cruise Velocity,
025 * Acceleration, and (optional) Jerk specified via the Motion Magic® configuration values.  This control mode
026 * does not use the Expo_kV or Expo_kA configs.
027 * <p>
028 * Target position can be changed on-the-fly and Motion Magic® will do its best to adjust the profile.  This
029 * control mode is duty cycle based, so relevant closed-loop gains will use fractional duty cycle for the
030 * numerator:  +1.0 represents full forward output.
031 */
032public final class MotionMagicDutyCycle implements ControlRequest, Cloneable {
033    /**
034     * Position to drive toward in rotations.
035     * 
036     * <ul>
037     *   <li> Units: rotations
038     * </ul>
039     * 
040     */
041    public double Position;
042    /**
043     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
044     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
045     * false to use trapezoidal commutation.
046     * <p>
047     * FOC improves motor performance by leveraging torque (current) control. 
048     * However, this may be inconvenient for applications that require specifying
049     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
050     * combines the performances gains of FOC while still allowing applications to
051     * provide duty cycle or voltage demand.  This not to be confused with simple
052     * sinusoidal control or phase voltage control which lacks the performance
053     * gains.
054     */
055    public boolean EnableFOC = true;
056    /**
057     * Feedforward to apply in fractional units between -1 and +1. This is added to
058     * the output of the onboard feedforward terms.
059     * 
060     * <ul>
061     *   <li> Units: fractional
062     * </ul>
063     * 
064     */
065    public double FeedForward = 0.0;
066    /**
067     * Select which gains are applied by selecting the slot.  Use the configuration
068     * api to set the gain values for the selected slot before enabling this
069     * feature. Slot must be within [0,2].
070     */
071    public int Slot = 0;
072    /**
073     * Set to true to static-brake the rotor when output is zero (or within
074     * deadband).  Set to false to use the NeutralMode configuration setting
075     * (default). This flag exists to provide the fundamental behavior of this
076     * control when output is zero, which is to provide 0V to the motor.
077     */
078    public boolean OverrideBrakeDurNeutral = false;
079    /**
080     * Set to true to force forward limiting.  This allows users to use other limit
081     * switch sensors connected to robot controller.  This also allows use of active
082     * sensors that require external power.
083     */
084    public boolean LimitForwardMotion = false;
085    /**
086     * Set to true to force reverse limiting.  This allows users to use other limit
087     * switch sensors connected to robot controller.  This also allows use of active
088     * sensors that require external power.
089     */
090    public boolean LimitReverseMotion = false;
091    /**
092     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
093     * LimitReverseMotion parameters, instead allowing motion.
094     * <p>
095     * This can be useful on mechanisms such as an intake/feeder, where a limit
096     * switch stops motion while intaking but should be ignored when feeding to a
097     * shooter.
098     * <p>
099     * The hardware limit faults and Forward/ReverseLimit signals will still report
100     * the values of the limit switches regardless of this parameter.
101     */
102    public boolean IgnoreHardwareLimits = false;
103    /**
104     * Set to true to ignore software limits, instead allowing motion.
105     * <p>
106     * This can be useful when calibrating the zero point of a mechanism such as an
107     * elevator.
108     * <p>
109     * The software limit faults will still report the values of the software limits
110     * regardless of this parameter.
111     */
112    public boolean IgnoreSoftwareLimits = false;
113    /**
114     * Set to true to delay applying this control request until a timesync boundary
115     * (requires Phoenix Pro and CANivore). This eliminates the impact of
116     * nondeterministic network delays in exchange for a larger but deterministic
117     * control latency.
118     * <p>
119     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
120     * Additionally, when this is enabled, the UpdateFreqHz of this request should
121     * be set to 0 Hz.
122     */
123    public boolean UseTimesync = false;
124
125    /**
126     * The frequency at which this control will update.
127     * This is designated in Hertz, with a minimum of 20 Hz
128     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
129     * Some update frequencies are not supported and will be
130     * promoted up to the next highest supported frequency.
131     * <p>
132     * If this field is set to 0 Hz, the control request will
133     * be sent immediately as a one-shot frame. This may be useful
134     * for advanced applications that require outputs to be
135     * synchronized with data acquisition. In this case, we
136     * recommend not exceeding 50 ms between control calls.
137     */
138    public double UpdateFreqHz = 100;
139
140    /**
141     * Requests Motion Magic® to target a final position using a motion profile. 
142     * Users can optionally provide a duty cycle feedforward.
143     * <p>
144     * Motion Magic® produces a motion profile in real-time while attempting to
145     * honor the Cruise Velocity, Acceleration, and (optional) Jerk specified via
146     * the Motion Magic® configuration values.  This control mode does not use the
147     * Expo_kV or Expo_kA configs.
148     * <p>
149     * Target position can be changed on-the-fly and Motion Magic® will do its best
150     * to adjust the profile.  This control mode is duty cycle based, so relevant
151     * closed-loop gains will use fractional duty cycle for the numerator:  +1.0
152     * represents full forward output.
153     * 
154     * @param Position Position to drive toward in rotations.
155     */
156    public MotionMagicDutyCycle(double Position) {
157        this.Position = Position;
158    }
159
160    /**
161     * Requests Motion Magic® to target a final position using a motion profile. 
162     * Users can optionally provide a duty cycle feedforward.
163     * <p>
164     * Motion Magic® produces a motion profile in real-time while attempting to
165     * honor the Cruise Velocity, Acceleration, and (optional) Jerk specified via
166     * the Motion Magic® configuration values.  This control mode does not use the
167     * Expo_kV or Expo_kA configs.
168     * <p>
169     * Target position can be changed on-the-fly and Motion Magic® will do its best
170     * to adjust the profile.  This control mode is duty cycle based, so relevant
171     * closed-loop gains will use fractional duty cycle for the numerator:  +1.0
172     * represents full forward output.
173     * 
174     * @param Position Position to drive toward in rotations.
175     */
176    public MotionMagicDutyCycle(Angle Position) {
177        this(Position.in(Rotations));
178    }
179
180    @Override
181    public String getName() {
182        return "MotionMagicDutyCycle";
183    }
184
185    @Override
186    public String toString() {
187        String ss = "Control: MotionMagicDutyCycle\n";
188        ss += "    Position: " + Position + " rotations" + "\n";
189        ss += "    EnableFOC: " + EnableFOC + "\n";
190        ss += "    FeedForward: " + FeedForward + " fractional" + "\n";
191        ss += "    Slot: " + Slot + "\n";
192        ss += "    OverrideBrakeDurNeutral: " + OverrideBrakeDurNeutral + "\n";
193        ss += "    LimitForwardMotion: " + LimitForwardMotion + "\n";
194        ss += "    LimitReverseMotion: " + LimitReverseMotion + "\n";
195        ss += "    IgnoreHardwareLimits: " + IgnoreHardwareLimits + "\n";
196        ss += "    IgnoreSoftwareLimits: " + IgnoreSoftwareLimits + "\n";
197        ss += "    UseTimesync: " + UseTimesync + "\n";
198        return ss;
199    }
200
201    @Override
202    public StatusCode sendRequest(String network, int deviceHash) {
203        return StatusCode.valueOf(ControlJNI.JNI_RequestControlMotionMagicDutyCycle(
204                network, deviceHash, UpdateFreqHz, Position, EnableFOC, FeedForward, Slot, OverrideBrakeDurNeutral, LimitForwardMotion, LimitReverseMotion, IgnoreHardwareLimits, IgnoreSoftwareLimits, UseTimesync));
205    }
206
207    /**
208     * Gets information about this control request.
209     *
210     * @return Map of control parameter names and corresponding applied values
211     */
212    @Override
213    public Map<String, String> getControlInfo() {
214        var controlInfo = new HashMap<String, String>();
215        controlInfo.put("Name", getName());
216        controlInfo.put("Position", String.valueOf(this.Position));
217        controlInfo.put("EnableFOC", String.valueOf(this.EnableFOC));
218        controlInfo.put("FeedForward", String.valueOf(this.FeedForward));
219        controlInfo.put("Slot", String.valueOf(this.Slot));
220        controlInfo.put("OverrideBrakeDurNeutral", String.valueOf(this.OverrideBrakeDurNeutral));
221        controlInfo.put("LimitForwardMotion", String.valueOf(this.LimitForwardMotion));
222        controlInfo.put("LimitReverseMotion", String.valueOf(this.LimitReverseMotion));
223        controlInfo.put("IgnoreHardwareLimits", String.valueOf(this.IgnoreHardwareLimits));
224        controlInfo.put("IgnoreSoftwareLimits", String.valueOf(this.IgnoreSoftwareLimits));
225        controlInfo.put("UseTimesync", String.valueOf(this.UseTimesync));
226        return controlInfo;
227    }
228    
229    /**
230     * Modifies this Control Request's Position parameter and returns itself for
231     * method-chaining and easier to use request API.
232     * <p>
233     * Position to drive toward in rotations.
234     * 
235     * <ul>
236     *   <li> Units: rotations
237     * </ul>
238     * 
239     *
240     * @param newPosition Parameter to modify
241     * @return Itself
242     */
243    public MotionMagicDutyCycle withPosition(double newPosition) {
244        Position = newPosition;
245        return this;
246    }
247    
248    /**
249     * Modifies this Control Request's Position parameter and returns itself for
250     * method-chaining and easier to use request API.
251     * <p>
252     * Position to drive toward in rotations.
253     * 
254     * <ul>
255     *   <li> Units: rotations
256     * </ul>
257     * 
258     *
259     * @param newPosition Parameter to modify
260     * @return Itself
261     */
262    public MotionMagicDutyCycle withPosition(Angle newPosition) {
263        Position = newPosition.in(Rotations);
264        return this;
265    }
266    
267    /**
268     * Helper method to get this Control Request's Position parameter converted
269     * to a unit type. If not using the Java units library, {@link #Position}
270     * can be accessed directly instead.
271     * <p>
272     * Position to drive toward in rotations.
273     * 
274     * <ul>
275     *   <li> Units: rotations
276     * </ul>
277     * 
278     *
279     * @return Position
280     */
281    public Angle getPositionMeasure() {
282        return Rotations.of(Position);
283    }
284    
285    /**
286     * Modifies this Control Request's EnableFOC parameter and returns itself for
287     * method-chaining and easier to use request API.
288     * <p>
289     * Set to true to use FOC commutation (requires Phoenix Pro), which increases
290     * peak power by ~15% on supported devices (see {@link SupportsFOC}). Set to
291     * false to use trapezoidal commutation.
292     * <p>
293     * FOC improves motor performance by leveraging torque (current) control. 
294     * However, this may be inconvenient for applications that require specifying
295     * duty cycle or voltage.  CTR-Electronics has developed a hybrid method that
296     * combines the performances gains of FOC while still allowing applications to
297     * provide duty cycle or voltage demand.  This not to be confused with simple
298     * sinusoidal control or phase voltage control which lacks the performance
299     * gains.
300     *
301     * @param newEnableFOC Parameter to modify
302     * @return Itself
303     */
304    public MotionMagicDutyCycle withEnableFOC(boolean newEnableFOC) {
305        EnableFOC = newEnableFOC;
306        return this;
307    }
308    
309    /**
310     * Modifies this Control Request's FeedForward parameter and returns itself for
311     * method-chaining and easier to use request API.
312     * <p>
313     * Feedforward to apply in fractional units between -1 and +1. This is added to
314     * the output of the onboard feedforward terms.
315     * 
316     * <ul>
317     *   <li> Units: fractional
318     * </ul>
319     * 
320     *
321     * @param newFeedForward Parameter to modify
322     * @return Itself
323     */
324    public MotionMagicDutyCycle withFeedForward(double newFeedForward) {
325        FeedForward = newFeedForward;
326        return this;
327    }
328    
329    /**
330     * Modifies this Control Request's Slot parameter and returns itself for
331     * method-chaining and easier to use request API.
332     * <p>
333     * Select which gains are applied by selecting the slot.  Use the configuration
334     * api to set the gain values for the selected slot before enabling this
335     * feature. Slot must be within [0,2].
336     *
337     * @param newSlot Parameter to modify
338     * @return Itself
339     */
340    public MotionMagicDutyCycle withSlot(int newSlot) {
341        Slot = newSlot;
342        return this;
343    }
344    
345    /**
346     * Modifies this Control Request's OverrideBrakeDurNeutral parameter and returns itself for
347     * method-chaining and easier to use request API.
348     * <p>
349     * Set to true to static-brake the rotor when output is zero (or within
350     * deadband).  Set to false to use the NeutralMode configuration setting
351     * (default). This flag exists to provide the fundamental behavior of this
352     * control when output is zero, which is to provide 0V to the motor.
353     *
354     * @param newOverrideBrakeDurNeutral Parameter to modify
355     * @return Itself
356     */
357    public MotionMagicDutyCycle withOverrideBrakeDurNeutral(boolean newOverrideBrakeDurNeutral) {
358        OverrideBrakeDurNeutral = newOverrideBrakeDurNeutral;
359        return this;
360    }
361    
362    /**
363     * Modifies this Control Request's LimitForwardMotion parameter and returns itself for
364     * method-chaining and easier to use request API.
365     * <p>
366     * Set to true to force forward limiting.  This allows users to use other limit
367     * switch sensors connected to robot controller.  This also allows use of active
368     * sensors that require external power.
369     *
370     * @param newLimitForwardMotion Parameter to modify
371     * @return Itself
372     */
373    public MotionMagicDutyCycle withLimitForwardMotion(boolean newLimitForwardMotion) {
374        LimitForwardMotion = newLimitForwardMotion;
375        return this;
376    }
377    
378    /**
379     * Modifies this Control Request's LimitReverseMotion parameter and returns itself for
380     * method-chaining and easier to use request API.
381     * <p>
382     * Set to true to force reverse limiting.  This allows users to use other limit
383     * switch sensors connected to robot controller.  This also allows use of active
384     * sensors that require external power.
385     *
386     * @param newLimitReverseMotion Parameter to modify
387     * @return Itself
388     */
389    public MotionMagicDutyCycle withLimitReverseMotion(boolean newLimitReverseMotion) {
390        LimitReverseMotion = newLimitReverseMotion;
391        return this;
392    }
393    
394    /**
395     * Modifies this Control Request's IgnoreHardwareLimits parameter and returns itself for
396     * method-chaining and easier to use request API.
397     * <p>
398     * Set to true to ignore hardware limit switches and the LimitForwardMotion and
399     * LimitReverseMotion parameters, instead allowing motion.
400     * <p>
401     * This can be useful on mechanisms such as an intake/feeder, where a limit
402     * switch stops motion while intaking but should be ignored when feeding to a
403     * shooter.
404     * <p>
405     * The hardware limit faults and Forward/ReverseLimit signals will still report
406     * the values of the limit switches regardless of this parameter.
407     *
408     * @param newIgnoreHardwareLimits Parameter to modify
409     * @return Itself
410     */
411    public MotionMagicDutyCycle withIgnoreHardwareLimits(boolean newIgnoreHardwareLimits) {
412        IgnoreHardwareLimits = newIgnoreHardwareLimits;
413        return this;
414    }
415    
416    /**
417     * Modifies this Control Request's IgnoreSoftwareLimits parameter and returns itself for
418     * method-chaining and easier to use request API.
419     * <p>
420     * Set to true to ignore software limits, instead allowing motion.
421     * <p>
422     * This can be useful when calibrating the zero point of a mechanism such as an
423     * elevator.
424     * <p>
425     * The software limit faults will still report the values of the software limits
426     * regardless of this parameter.
427     *
428     * @param newIgnoreSoftwareLimits Parameter to modify
429     * @return Itself
430     */
431    public MotionMagicDutyCycle withIgnoreSoftwareLimits(boolean newIgnoreSoftwareLimits) {
432        IgnoreSoftwareLimits = newIgnoreSoftwareLimits;
433        return this;
434    }
435    
436    /**
437     * Modifies this Control Request's UseTimesync parameter and returns itself for
438     * method-chaining and easier to use request API.
439     * <p>
440     * Set to true to delay applying this control request until a timesync boundary
441     * (requires Phoenix Pro and CANivore). This eliminates the impact of
442     * nondeterministic network delays in exchange for a larger but deterministic
443     * control latency.
444     * <p>
445     * This requires setting the ControlTimesyncFreqHz config in MotorOutputConfigs.
446     * Additionally, when this is enabled, the UpdateFreqHz of this request should
447     * be set to 0 Hz.
448     *
449     * @param newUseTimesync Parameter to modify
450     * @return Itself
451     */
452    public MotionMagicDutyCycle withUseTimesync(boolean newUseTimesync) {
453        UseTimesync = newUseTimesync;
454        return this;
455    }
456
457    /**
458     * Sets the frequency at which this control will update.
459     * This is designated in Hertz, with a minimum of 20 Hz
460     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
461     * Some update frequencies are not supported and will be
462     * promoted up to the next highest supported frequency.
463     * <p>
464     * If this field is set to 0 Hz, the control request will
465     * be sent immediately as a one-shot frame. This may be useful
466     * for advanced applications that require outputs to be
467     * synchronized with data acquisition. In this case, we
468     * recommend not exceeding 50 ms between control calls.
469     *
470     * @param newUpdateFreqHz Parameter to modify
471     * @return Itself
472     */
473    @Override
474    public MotionMagicDutyCycle withUpdateFreqHz(double newUpdateFreqHz) {
475        UpdateFreqHz = newUpdateFreqHz;
476        return this;
477    }
478
479    /**
480     * Sets the frequency at which this control will update.
481     * This is designated in Hertz, with a minimum of 20 Hz
482     * (every 50 ms) and a maximum of 1000 Hz (every 1 ms).
483     * Some update frequencies are not supported and will be
484     * promoted up to the next highest supported frequency.
485     * <p>
486     * If this field is set to 0 Hz, the control request will
487     * be sent immediately as a one-shot frame. This may be useful
488     * for advanced applications that require outputs to be
489     * synchronized with data acquisition. In this case, we
490     * recommend not exceeding 50 ms between control calls.
491     *
492     * @param newUpdateFreqHz Parameter to modify
493     * @return Itself
494     */
495    @Override
496    public MotionMagicDutyCycle withUpdateFreqHz(Frequency newUpdateFreqHz) {
497        UpdateFreqHz = newUpdateFreqHz.in(Hertz);
498        return this;
499    }
500
501    @Override
502    public MotionMagicDutyCycle clone() {
503        try {
504            return (MotionMagicDutyCycle)super.clone();
505        } catch (CloneNotSupportedException ex) {
506            /* this should never happen */
507            throw new RuntimeException(ex);
508        }
509    }
510}
511