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