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