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